home *** CD-ROM | disk | FTP | other *** search
/ Team Palmtops 7 / Palmtops_numero07.iso / WinCE / SDKWindowsCE / HandHeldPCPro30 / sdk.exe / Jupiter SDK / data1.cab / ATL / include / Atldbcli.h < prev    next >
Encoding:
C/C++ Source or Header  |  1999-02-19  |  97.8 KB  |  3,663 lines

  1. // This is a part of the Active Template Library.
  2. // Copyright (C) 1996-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Active Template Library Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Active Template Library product.
  10.  
  11. // ATLDBCLI.H : ATL consumer code for OLEDB
  12.  
  13. #ifndef __ATLDBCLI_H_
  14. #define __ATLDBCLI_H_
  15.  
  16. #ifndef __cplusplus
  17.     #error ATL requires C++ compilation (use a .cpp suffix)
  18. #endif
  19.  
  20. #ifndef _ATLBASE_H
  21. #include <atlbase.h>
  22. #endif
  23.  
  24. #ifndef __oledb_h__
  25. #include <oledb.h>
  26. #endif // __oledb_h__
  27.  
  28. #include <msdaguid.h>
  29. #include <msdasc.h>
  30.  
  31. namespace ATL
  32. {
  33.  
  34. #define DEFINE_OLEDB_TYPE_FUNCTION(ctype, oledbtype) \
  35.     inline DBTYPE _GetOleDBType(ctype&) \
  36.     { \
  37.         return oledbtype; \
  38.     }
  39.     inline DBTYPE _GetOleDBType(CHAR[])
  40.     {
  41.         return DBTYPE_STR;
  42.     }
  43.     inline DBTYPE _GetOleDBType(WCHAR[])
  44.     {
  45.         return DBTYPE_WSTR;
  46.     }
  47.  
  48.     DEFINE_OLEDB_TYPE_FUNCTION(signed char        ,DBTYPE_I1)
  49.     DEFINE_OLEDB_TYPE_FUNCTION(SHORT            ,DBTYPE_I2)        // DBTYPE_BOOL
  50.     DEFINE_OLEDB_TYPE_FUNCTION(int                ,DBTYPE_I4)
  51.     DEFINE_OLEDB_TYPE_FUNCTION(LONG                ,DBTYPE_I4)        // DBTYPE_ERROR (SCODE)
  52.     DEFINE_OLEDB_TYPE_FUNCTION(LARGE_INTEGER    ,DBTYPE_I8)        // DBTYPE_CY
  53.     DEFINE_OLEDB_TYPE_FUNCTION(BYTE                ,DBTYPE_UI1)
  54.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned short    ,DBTYPE_UI2)
  55.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned int        ,DBTYPE_UI4)
  56.     DEFINE_OLEDB_TYPE_FUNCTION(unsigned long    ,DBTYPE_UI4)
  57.     DEFINE_OLEDB_TYPE_FUNCTION(ULARGE_INTEGER    ,DBTYPE_UI8)
  58.     DEFINE_OLEDB_TYPE_FUNCTION(float            ,DBTYPE_R4)
  59.     DEFINE_OLEDB_TYPE_FUNCTION(double            ,DBTYPE_R8)        // DBTYPE_DATE
  60.     DEFINE_OLEDB_TYPE_FUNCTION(DECIMAL            ,DBTYPE_DECIMAL)
  61.     DEFINE_OLEDB_TYPE_FUNCTION(DB_NUMERIC        ,DBTYPE_NUMERIC)
  62.     DEFINE_OLEDB_TYPE_FUNCTION(BYTE*            ,DBTYPE_BYTES)
  63. //    DEFINE_OLEDB_TYPE_FUNCTION(CHAR*            ,DBTYPE_STR | DBTYPE_BYREF)
  64. //    DEFINE_OLEDB_TYPE_FUNCTION(WCHAR*            ,DBTYPE_WSTR | DBTYPE_BYREF)    // DBTYPE_BSTR
  65.     DEFINE_OLEDB_TYPE_FUNCTION(VARIANT            ,DBTYPE_VARIANT)
  66.     DEFINE_OLEDB_TYPE_FUNCTION(IDispatch*        ,DBTYPE_IDISPATCH)
  67.     DEFINE_OLEDB_TYPE_FUNCTION(IUnknown*        ,DBTYPE_IUNKNOWN)
  68.     DEFINE_OLEDB_TYPE_FUNCTION(GUID                ,DBTYPE_GUID)
  69.     DEFINE_OLEDB_TYPE_FUNCTION(SAFEARRAY*        ,DBTYPE_ARRAY)
  70.     DEFINE_OLEDB_TYPE_FUNCTION(DBVECTOR            ,DBTYPE_VECTOR)
  71.     DEFINE_OLEDB_TYPE_FUNCTION(DBDATE            ,DBTYPE_DBDATE)
  72.     DEFINE_OLEDB_TYPE_FUNCTION(DBTIME            ,DBTYPE_DBTIME)
  73.     DEFINE_OLEDB_TYPE_FUNCTION(DBTIMESTAMP        ,DBTYPE_DBTIMESTAMP)
  74.  
  75. // Internal structure containing the accessor handle and a flag
  76. // indicating whether the data for the accessor is automatically
  77. // retrieved
  78. struct _ATL_ACCESSOR_INFO
  79. {
  80.     HACCESSOR   hAccessor;
  81.     bool        bAutoAccessor;
  82. };
  83.  
  84. class _CNoOutputColumns
  85. {
  86. public:
  87.     static bool HasOutputColumns()
  88.     {
  89.         return false;
  90.     }
  91.     static ULONG _GetNumAccessors()
  92.     {
  93.         return 0;
  94.     }
  95.     static HRESULT _GetBindEntries(ULONG*, DBBINDING*, ULONG, bool*, BYTE* pBuffer = NULL)
  96.     {
  97.         pBuffer;
  98.         return E_FAIL;
  99.     }
  100. };
  101.  
  102. class _CNoParameters
  103. {
  104. public:
  105.     static bool HasParameters()
  106.     {
  107.         return false;
  108.     }
  109.     static HRESULT _GetParamEntries(ULONG*, DBBINDING*, BYTE* pBuffer = NULL)
  110.     {
  111.         pBuffer;
  112.         return E_FAIL;
  113.     }
  114. };
  115.  
  116. class _CNoCommand
  117. {
  118. public:
  119.     static HRESULT GetDefaultCommand(LPCTSTR* /*ppszCommand*/)
  120.     {
  121.         return S_OK;
  122.     }
  123. };
  124.  
  125. typedef _CNoOutputColumns    _OutputColumnsClass;
  126. typedef _CNoParameters        _ParamClass;
  127. typedef _CNoCommand            _CommandClass;
  128.  
  129. #define BEGIN_ACCESSOR_MAP(x, num) \
  130.     public: \
  131.     typedef x _classtype; \
  132.     typedef x _OutputColumnsClass; \
  133.     static ULONG _GetNumAccessors() { return num; } \
  134.     static bool HasOutputColumns() { return true; } \
  135.     /* If pBindings == NULL means we only return the column number */ \
  136.     /* If pBuffer != NULL then it points to the accessor buffer and */ \
  137.     /* we release any appropriate memory e.g. BSTR's or interface pointers */ \
  138.     inline static HRESULT _GetBindEntries(ULONG* pColumns, DBBINDING *pBinding, ULONG nAccessor, bool* pAuto, BYTE* pBuffer = NULL) \
  139.     { \
  140.         ATLASSERT(pColumns != NULL); \
  141.         DBPARAMIO eParamIO = DBPARAMIO_NOTPARAM; \
  142.         ULONG nColumns = 0; \
  143.         pBuffer;
  144.  
  145. #define BEGIN_ACCESSOR(num, bAuto) \
  146.     if (nAccessor == num) \
  147.     { \
  148.         if (pBinding != NULL) \
  149.             *pAuto = bAuto;
  150.  
  151. #define END_ACCESSOR() \
  152.     } \
  153.     else
  154.  
  155. #define END_ACCESSOR_MAP() \
  156.         ; \
  157.         *pColumns = nColumns; \
  158.         return S_OK; \
  159.     }
  160.  
  161. #define BEGIN_COLUMN_MAP(x) \
  162.     BEGIN_ACCESSOR_MAP(x, 1) \
  163.         BEGIN_ACCESSOR(0, true)
  164.  
  165. #define END_COLUMN_MAP() \
  166.         END_ACCESSOR() \
  167.     END_ACCESSOR_MAP()
  168.  
  169. #define offsetbuf(m) offsetof(_classtype, m)
  170. #define _OLEDB_TYPE(data) _GetOleDBType(((_classtype*)0)->data)
  171. #define _SIZE_TYPE(data) sizeof(((_classtype*)0)->data)
  172.  
  173. #define _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, dataOffset, lengthOffset, statusOffset) \
  174.     if (pBuffer != NULL) \
  175.     { \
  176.         CAccessorBase::FreeType(wType, pBuffer + dataOffset); \
  177.     } \
  178.     else if (pBinding != NULL) \
  179.     { \
  180.         CAccessorBase::Bind(pBinding, nOrdinal, wType, nLength, nPrecision, nScale, eParamIO, \
  181.             dataOffset, lengthOffset, statusOffset); \
  182.         pBinding++; \
  183.     } \
  184.     nColumns++;
  185.  
  186. #define COLUMN_ENTRY_EX(nOrdinal, wType, nLength, nPrecision, nScale, data, length, status) \
  187.     _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  188.  
  189. #define COLUMN_ENTRY_TYPE(nOrdinal, wType, data) \
  190.     COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, _SIZE_TYPE(data), data)
  191.  
  192. #define COLUMN_ENTRY_TYPE_SIZE(nOrdinal, wType, nLength, data) \
  193.     _COLUMN_ENTRY_CODE(nOrdinal, wType, nLength, 0, 0, offsetbuf(data), 0, 0)
  194.  
  195.  
  196. // Standard macros where type and size is worked out
  197. #define COLUMN_ENTRY(nOrdinal, data) \
  198.     COLUMN_ENTRY_TYPE(nOrdinal, _OLEDB_TYPE(data), data)
  199.  
  200. #define COLUMN_ENTRY_LENGTH(nOrdinal, data, length) \
  201.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), 0)
  202.  
  203. #define COLUMN_ENTRY_STATUS(nOrdinal, data, status) \
  204.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), 0, offsetbuf(status))
  205.     
  206. #define COLUMN_ENTRY_LENGTH_STATUS(nOrdinal, data, length, status) \
  207.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), 0, 0, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  208.  
  209.  
  210. // Follow macros are used if precision and scale need to be specified
  211. #define COLUMN_ENTRY_PS(nOrdinal, nPrecision, nScale, data) \
  212.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, 0)
  213.     
  214. #define COLUMN_ENTRY_PS_LENGTH(nOrdinal, nPrecision, nScale, data, length) \
  215.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), 0)
  216.  
  217. #define COLUMN_ENTRY_PS_STATUS(nOrdinal, nPrecision, nScale, data, status) \
  218.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), 0, offsetbuf(status))
  219.     
  220. #define COLUMN_ENTRY_PS_LENGTH_STATUS(nOrdinal, nPrecision, nScale, data, length, status) \
  221.     _COLUMN_ENTRY_CODE(nOrdinal, _OLEDB_TYPE(data), _SIZE_TYPE(data), nPrecision, nScale, offsetbuf(data), offsetbuf(length), offsetbuf(status))
  222.  
  223.  
  224. #define BOOKMARK_ENTRY(variable) \
  225.     COLUMN_ENTRY_TYPE_SIZE(0, DBTYPE_BYTES, _SIZE_TYPE(variable##.m_rgBuffer), variable##.m_rgBuffer)
  226.  
  227. #define BLOB_ENTRY(nOrdinal, IID, flags, data) \
  228.     if (pBuffer != NULL) \
  229.     { \
  230.         CAccessorBase::FreeType(DBTYPE_IUNKNOWN, pBuffer + offsetbuf(data)); \
  231.     } \
  232.     else if (pBinding != NULL) \
  233.     { \
  234.         DBOBJECT* pObject = NULL; \
  235.         ATLTRY(pObject = new DBOBJECT); \
  236.         if (pObject == NULL) \
  237.             return E_OUTOFMEMORY; \
  238.         pObject->dwFlags = flags; \
  239.         pObject->iid     = IID; \
  240.         CAccessorBase::Bind(pBinding, nOrdinal, DBTYPE_IUNKNOWN, sizeof(IUnknown*), 0, 0, eParamIO, \
  241.             offsetbuf(data), 0, 0, pObject); \
  242.         pBinding++; \
  243.     } \
  244.     nColumns++;
  245.  
  246. #define BEGIN_PARAM_MAP(x) \
  247.     public: \
  248.     typedef x _classtype; \
  249.     typedef x _ParamClass; \
  250.     static bool HasParameters() { return true; } \
  251.     static HRESULT _GetParamEntries(ULONG* pColumns, DBBINDING *pBinding, BYTE* pBuffer = NULL) \
  252.     { \
  253.         ATLASSERT(pColumns != NULL); \
  254.         DBPARAMIO eParamIO = DBPARAMIO_INPUT; \
  255.         int nColumns = 0; \
  256.         pBuffer;
  257.  
  258. #define END_PARAM_MAP() \
  259.         *pColumns = nColumns; \
  260.         return S_OK; \
  261.     }
  262.  
  263. #define SET_PARAM_TYPE(type) \
  264.     eParamIO = type;
  265.  
  266. #define DEFINE_COMMAND(x, szCommand) \
  267.     typedef x _CommandClass; \
  268.     static HRESULT GetDefaultCommand(LPCTSTR* ppszCommand) \
  269.     { \
  270.         *ppszCommand = szCommand; \
  271.         return S_OK; \
  272.     }
  273.  
  274.  
  275. ///////////////////////////////////////////////////////////////////////////
  276. // class CDBErrorInfo
  277.  
  278. class CDBErrorInfo
  279. {
  280. public:
  281.     // Use to get the number of error record when you want to explicitly check that
  282.     // the passed interface set the error information
  283.     HRESULT GetErrorRecords(IUnknown* pUnk, const IID& iid, ULONG* pcRecords)
  284.     {
  285.         CComPtr<ISupportErrorInfo> spSupportErrorInfo;
  286.         HRESULT hr = pUnk->QueryInterface(&spSupportErrorInfo);
  287.         if (FAILED(hr))
  288.             return hr;
  289.  
  290.         hr = spSupportErrorInfo->InterfaceSupportsErrorInfo(iid);
  291.         if (FAILED(hr))
  292.             return hr;
  293.  
  294.         return GetErrorRecords(pcRecords);
  295.     }
  296.     // Use to get the number of error records
  297.     HRESULT GetErrorRecords(ULONG* pcRecords)
  298.     {
  299.         HRESULT    hr;
  300.         CComPtr<IErrorInfo> spErrorInfo;
  301.         hr = ::GetErrorInfo(0, &spErrorInfo);
  302.         if (hr == S_FALSE)
  303.              return hr;
  304.  
  305.         spErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&m_spErrorRecords);
  306.         if (m_spErrorRecords == NULL)
  307.             return E_FAIL;
  308.  
  309.         return m_spErrorRecords->GetRecordCount(pcRecords);
  310.     }
  311.     // Get the error information for the passed record number. GetErrorRecords must
  312.     // be called before this function is called.
  313.     HRESULT GetAllErrorInfo(ULONG ulRecordNum, LCID lcid, BSTR* pbstrDescription,
  314.         BSTR* pbstrSource = NULL, GUID* pguid = NULL, DWORD* pdwHelpContext = NULL,
  315.         BSTR* pbstrHelpFile = NULL) const
  316.     {
  317.         ATLASSERT(m_spErrorRecords != NULL);
  318.         CComPtr<IErrorInfo> spErrorInfo;
  319.         HRESULT hr = m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, &spErrorInfo);
  320.         if (FAILED(hr))
  321.             return hr;
  322.  
  323.         if (pbstrDescription != NULL)
  324.             spErrorInfo->GetDescription(pbstrDescription);
  325.  
  326.         if (pguid != NULL)
  327.             spErrorInfo->GetGUID(pguid);
  328.  
  329.         if (pdwHelpContext != NULL)
  330.             spErrorInfo->GetHelpContext(pdwHelpContext);
  331.  
  332.         if (pbstrHelpFile != NULL)
  333.             spErrorInfo->GetHelpFile(pbstrHelpFile);
  334.  
  335.         if (pbstrSource != NULL)
  336.             spErrorInfo->GetSource(pbstrSource);
  337.  
  338.         return S_OK;
  339.     }
  340.     // Get the error information for the passed record number
  341.     HRESULT GetBasicErrorInfo(ULONG ulRecordNum, ERRORINFO* pErrorInfo) const
  342.     {
  343.         return m_spErrorRecords->GetBasicErrorInfo(ulRecordNum, pErrorInfo);
  344.     }
  345.     // Get the custom error object for the passed record number
  346.     HRESULT GetCustomErrorObject(ULONG ulRecordNum, REFIID riid, IUnknown** ppObject) const
  347.     {
  348.         return m_spErrorRecords->GetCustomErrorObject(ulRecordNum, riid, ppObject);
  349.     }
  350.     // Get the IErrorInfo interface for the passed record number
  351.     HRESULT GetErrorInfo(ULONG ulRecordNum, LCID lcid, IErrorInfo** ppErrorInfo) const
  352.     {
  353.         return m_spErrorRecords->GetErrorInfo(ulRecordNum, lcid, ppErrorInfo);
  354.     }
  355.     // Get the error parameters for the passed record number
  356.     HRESULT GetErrorParameters(ULONG ulRecordNum, DISPPARAMS* pdispparams) const
  357.     {
  358.         return m_spErrorRecords->GetErrorParameters(ulRecordNum, pdispparams);
  359.     }
  360.  
  361. // Implementation
  362.     CComPtr<IErrorRecords> m_spErrorRecords;
  363. };
  364.  
  365.  
  366. ///////////////////////////////////////////////////////////////////////////
  367. // class CDBPropSet
  368.  
  369. class CDBPropSet : public tagDBPROPSET
  370. {
  371. public:
  372.     CDBPropSet()
  373.     {
  374.         rgProperties    = NULL;
  375.         cProperties     = 0;
  376.     }
  377.     CDBPropSet(const GUID& guid)
  378.     {
  379.         rgProperties    = NULL;
  380.         cProperties     = 0;
  381.         guidPropertySet = guid;
  382.     }
  383.     CDBPropSet(const CDBPropSet& propset)
  384.     {
  385.         InternalCopy(propset);
  386.     }
  387.     ~CDBPropSet()
  388.     {
  389.         for (ULONG i = 0; i < cProperties; i++)
  390.             VariantClear(&rgProperties[i].vValue);
  391.  
  392.         CoTaskMemFree(rgProperties);
  393.     }
  394.     CDBPropSet& operator=(CDBPropSet& propset)
  395.     {
  396.         this->~CDBPropSet();
  397.         InternalCopy(propset);
  398.         return *this;
  399.     }
  400.     // Set the GUID of the property set this class represents.
  401.     // Use if you didn't pass the GUID to the constructor.
  402.     void SetGUID(const GUID& guid)
  403.     {
  404.         guidPropertySet = guid;
  405.     }
  406.     // Add the passed property to the property set
  407.     bool AddProperty(DWORD dwPropertyID, const VARIANT& var)
  408.     {
  409.         HRESULT hr;
  410.         if (!Add())
  411.             return false;
  412.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  413.         hr = ::VariantCopy(&(rgProperties[cProperties].vValue), const_cast<VARIANT*>(&var));
  414.         if (FAILED(hr))
  415.             return false;
  416.         cProperties++;
  417.         return true;
  418.     }
  419.     // Add the passed property to the property set
  420.     bool AddProperty(DWORD dwPropertyID, LPCSTR szValue)
  421.     {
  422.         USES_CONVERSION;
  423.         if (!Add())
  424.             return false;
  425.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  426.         rgProperties[cProperties].vValue.vt      = VT_BSTR;
  427.         rgProperties[cProperties].vValue.bstrVal = SysAllocString(A2COLE(szValue));
  428.         if (rgProperties[cProperties].vValue.bstrVal == NULL)
  429.             return false;
  430.         cProperties++;
  431.         return true;
  432.     }
  433.     // Add the passed property to the property set
  434.     bool AddProperty(DWORD dwPropertyID, LPCWSTR szValue)
  435.     {
  436.         USES_CONVERSION;
  437.         if (!Add())
  438.             return false;
  439.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  440.         rgProperties[cProperties].vValue.vt      = VT_BSTR;
  441.         rgProperties[cProperties].vValue.bstrVal = SysAllocString(W2COLE(szValue));
  442.         if (rgProperties[cProperties].vValue.bstrVal == NULL)
  443.             return false;
  444.         cProperties++;
  445.         return true;
  446.     }
  447.     // Add the passed property to the property set
  448.     bool AddProperty(DWORD dwPropertyID, bool bValue)
  449.     {
  450.         if (!Add())
  451.             return false;
  452.         rgProperties[cProperties].dwPropertyID   = dwPropertyID;
  453.         rgProperties[cProperties].vValue.vt      = VT_BOOL;
  454. #pragma warning(disable: 4310) // cast truncates constant value
  455.         rgProperties[cProperties].vValue.boolVal = (bValue) ? VARIANT_TRUE : VARIANT_FALSE;
  456. #pragma warning(default: 4310)
  457.         cProperties++;
  458.         return true;
  459.     }
  460.     // Add the passed property to the property set
  461.     bool AddProperty(DWORD dwPropertyID, BYTE bValue)
  462.     {
  463.         if (!Add())
  464.             return false;
  465.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  466.         rgProperties[cProperties].vValue.vt     = VT_UI1;
  467.         rgProperties[cProperties].vValue.bVal   = bValue;
  468.         cProperties++;
  469.         return true;
  470.     }
  471.     // Add the passed property to the property set
  472.     bool AddProperty(DWORD dwPropertyID, short nValue)
  473.     {
  474.         if (!Add())
  475.             return false;
  476.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  477.         rgProperties[cProperties].vValue.vt     = VT_I2;
  478.         rgProperties[cProperties].vValue.iVal   = nValue;
  479.         cProperties++;
  480.         return true;
  481.     }
  482.     // Add the passed property to the property set
  483.     bool AddProperty(DWORD dwPropertyID, long nValue)
  484.     {
  485.         if (!Add())
  486.             return false;
  487.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  488.         rgProperties[cProperties].vValue.vt     = VT_I4;
  489.         rgProperties[cProperties].vValue.lVal   = nValue;
  490.         cProperties++;
  491.         return true;
  492.     }
  493.     // Add the passed property to the property set
  494.     bool AddProperty(DWORD dwPropertyID, float fltValue)
  495.     {
  496.         if (!Add())
  497.             return false;
  498.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  499.         rgProperties[cProperties].vValue.vt     = VT_R4;
  500.         rgProperties[cProperties].vValue.fltVal = fltValue;
  501.         cProperties++;
  502.         return true;
  503.     }
  504.     // Add the passed property to the property set
  505.     bool AddProperty(DWORD dwPropertyID, double dblValue)
  506.     {
  507.         if (!Add())
  508.             return false;
  509.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  510.         rgProperties[cProperties].vValue.vt     = VT_R8;
  511.         rgProperties[cProperties].vValue.dblVal = dblValue;
  512.         cProperties++;
  513.         return true;
  514.     }
  515.     // Add the passed property to the property set
  516.     bool AddProperty(DWORD dwPropertyID, CY cyValue)
  517.     {
  518.         if (!Add())
  519.             return false;
  520.         rgProperties[cProperties].dwPropertyID  = dwPropertyID;
  521.         rgProperties[cProperties].vValue.vt     = VT_CY;
  522.         rgProperties[cProperties].vValue.cyVal  = cyValue;
  523.         cProperties++;
  524.         return true;
  525.     }
  526. // Implementation
  527.     // Create memory to add a new property
  528.     bool Add()
  529.     {
  530.         rgProperties = (DBPROP*)CoTaskMemRealloc(rgProperties, (cProperties + 1) * sizeof(DBPROP));
  531.         if (rgProperties != NULL)
  532.         {
  533.             rgProperties[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
  534.             rgProperties[cProperties].colid     = DB_NULLID;
  535.             rgProperties[cProperties].vValue.vt = VT_EMPTY;
  536.             return true;
  537.         }
  538.         else
  539.             return false;
  540.     }
  541.     // Copies in the passed value now it this value been cleared
  542.     void InternalCopy(const CDBPropSet& propset)
  543.     {
  544.         cProperties        = propset.cProperties;
  545.         guidPropertySet = propset.guidPropertySet;
  546.         rgProperties    = (DBPROP*)CoTaskMemAlloc(cProperties * sizeof(DBPROP));
  547.         if (rgProperties != NULL)
  548.         {
  549.             for (ULONG i = 0; i < cProperties; i++)
  550.             {
  551.                 rgProperties[i].dwPropertyID = propset.rgProperties[i].dwPropertyID;
  552.                 rgProperties[i].dwOptions     = DBPROPOPTIONS_REQUIRED;
  553.                 rgProperties[i].colid         = DB_NULLID;
  554.                 rgProperties[i].vValue.vt     = VT_EMPTY;
  555.                 VariantCopy(&rgProperties[i].vValue, &propset.rgProperties[i].vValue);
  556.             }
  557.         }
  558.         else
  559.         {
  560.             // The memory allocation failed so set the count
  561.             // of properties to zero
  562.             cProperties = 0;
  563.         }
  564.     }
  565. };
  566.  
  567.  
  568. ///////////////////////////////////////////////////////////////////////////
  569. // class CDBPropIDSet
  570.  
  571. class CDBPropIDSet : public tagDBPROPIDSET
  572. {
  573. // Constructors and Destructors
  574. public:
  575.     CDBPropIDSet()
  576.     {
  577.         rgPropertyIDs   = NULL;
  578.         cPropertyIDs    = 0;
  579.     }
  580.     CDBPropIDSet(const GUID& guid)
  581.     {
  582.         rgPropertyIDs   = NULL;
  583.         cPropertyIDs    = 0;
  584.         guidPropertySet = guid;
  585.     }
  586.     CDBPropIDSet(const CDBPropIDSet& propidset)
  587.     {
  588.         InternalCopy(propidset);
  589.     }
  590.     ~CDBPropIDSet()
  591.     {
  592.         if (rgPropertyIDs != NULL)
  593.             free(rgPropertyIDs);
  594.     }
  595.     CDBPropIDSet& operator=(CDBPropIDSet& propset)
  596.     {
  597.         this->~CDBPropIDSet();
  598.         InternalCopy(propset);
  599.         return *this;
  600.     }
  601.     // Set the GUID of the property ID set
  602.     void SetGUID(const GUID& guid)
  603.     {
  604.         guidPropertySet = guid;
  605.     }
  606.     // Add a property ID to the set
  607.     bool AddPropertyID(DBPROPID propid)
  608.     {
  609.         if (!Add())
  610.             return false;
  611.         rgPropertyIDs[cPropertyIDs] = propid;
  612.         cPropertyIDs++;
  613.         return true;
  614.     }
  615. // Implementation
  616.     bool Add()
  617.     {
  618.         rgPropertyIDs = (DBPROPID*)realloc(rgPropertyIDs, (cPropertyIDs + 1) * sizeof(DBPROPID));
  619.         return (rgPropertyIDs != NULL) ? true : false;
  620.     }
  621.     void InternalCopy(const CDBPropIDSet& propidset)
  622.     {
  623.         cPropertyIDs    = propidset.cPropertyIDs;
  624.         guidPropertySet = propidset.guidPropertySet;
  625.         rgPropertyIDs    = (DBPROPID*)malloc(cPropertyIDs * sizeof(DBPROPID));
  626.         if (rgPropertyIDs != NULL)
  627.         {
  628.             for (ULONG i = 0; i < cPropertyIDs; i++)
  629.                 rgPropertyIDs[i] = propidset.rgPropertyIDs[i];
  630.         }
  631.         else
  632.         {
  633.             // The memory allocation failed so set the count
  634.             // of properties to zero
  635.             cPropertyIDs = 0;
  636.         }
  637.     }
  638. };
  639.  
  640.  
  641. ///////////////////////////////////////////////////////////////////////////
  642. // class CBookmarkBase
  643.  
  644. class ATL_NO_VTABLE CBookmarkBase
  645. {
  646. public:
  647.     virtual ULONG GetSize() const = 0;
  648.     virtual BYTE* GetBuffer() const = 0;
  649. };
  650.  
  651.  
  652. ///////////////////////////////////////////////////////////////////////////
  653. // class CBookmark
  654.  
  655. template <ULONG nSize = 0>
  656. class CBookmark : public CBookmarkBase
  657. {
  658. public:
  659.     virtual ULONG   GetSize() const { return nSize; }
  660.     virtual BYTE*   GetBuffer() const { return (BYTE*)m_rgBuffer; }
  661.  
  662. // Implementation
  663.     BYTE m_rgBuffer[nSize];
  664. };
  665.  
  666.  
  667. // Size of 0 means that the memory for the bookmark will be allocated
  668. // at run time.
  669. template <>
  670. class CBookmark<0> : public CBookmarkBase
  671. {
  672. public:
  673.     CBookmark()
  674.     {
  675.         m_nSize = 0;
  676.         m_pBuffer = NULL;
  677.     }
  678.     CBookmark(ULONG nSize)
  679.     {
  680.         m_pBuffer = NULL;
  681.         ATLTRY(m_pBuffer = new BYTE[nSize]);
  682.         m_nSize = (m_pBuffer == NULL) ? 0 : nSize;
  683.     }
  684.     ~CBookmark()
  685.     {
  686.         delete [] m_pBuffer;
  687.     }
  688.     CBookmark& operator=(const CBookmark& bookmark)
  689.     {
  690.         SetBookmark(bookmark.GetSize(), bookmark.GetBuffer());
  691.         return *this;
  692.     }
  693.     virtual ULONG GetSize() const { return m_nSize; }
  694.     virtual BYTE* GetBuffer() const { return m_pBuffer; }
  695.     // Sets the bookmark to the passed value
  696.     HRESULT SetBookmark(ULONG nSize, BYTE* pBuffer)
  697.     {
  698.         ATLASSERT(pBuffer != NULL);
  699.         delete [] m_pBuffer;
  700.         m_pBuffer = NULL;
  701.         ATLTRY(m_pBuffer = new BYTE[nSize]);
  702.         if (m_pBuffer != NULL)
  703.         {
  704.             memcpy(m_pBuffer, pBuffer, nSize);
  705.             m_nSize = nSize;
  706.             return S_OK;
  707.         }
  708.         else
  709.         {
  710.             m_nSize = 0;
  711.             return E_OUTOFMEMORY;
  712.         }
  713.     }
  714.     ULONG   m_nSize;
  715.     BYTE*   m_pBuffer;
  716. };
  717.  
  718.  
  719. ///////////////////////////////////////////////////////////////////////////
  720. // class CAccessorBase
  721.  
  722. class CAccessorBase
  723. {
  724. public:
  725.     CAccessorBase()
  726.     {
  727.         m_pAccessorInfo  = NULL;
  728.         m_nAccessors     = 0;
  729.         m_pBuffer        = NULL;
  730.     }
  731.     void Close()
  732.     {
  733.         // If Close is called then ReleaseAccessors must have been
  734.         // called first
  735.         ATLASSERT(m_nAccessors == 0);
  736.         ATLASSERT(m_pAccessorInfo == NULL);
  737.     }
  738.     // Get the number of accessors that have been created
  739.     ULONG GetNumAccessors() const { return m_nAccessors; }
  740.     // Get the handle of the passed accessor (offset from 0)
  741.     HACCESSOR GetHAccessor(ULONG nAccessor) const
  742.     {
  743.         ATLASSERT(nAccessor<m_nAccessors);
  744.         return m_pAccessorInfo[nAccessor].hAccessor;
  745.     };
  746.     // Called during Close to release the accessor information
  747.     HRESULT ReleaseAccessors(IUnknown* pUnk)
  748.     {
  749.         ATLASSERT(pUnk != NULL);
  750.         HRESULT hr = S_OK;
  751.         if (m_nAccessors > 0)
  752.         {
  753.             CComPtr<IAccessor> spAccessor;
  754.             hr = pUnk->QueryInterface(IID_IAccessor, (void**)&spAccessor);
  755.             if (SUCCEEDED(hr))
  756.             {
  757.                 ATLASSERT(m_pAccessorInfo != NULL);
  758.                 for (ULONG i = 0; i < m_nAccessors; i++)
  759.                     spAccessor->ReleaseAccessor(m_pAccessorInfo[i].hAccessor, NULL);
  760.             }
  761.             m_nAccessors = 0;
  762.             delete [] m_pAccessorInfo;
  763.             m_pAccessorInfo = NULL;
  764.         }
  765.         return hr;
  766.     }
  767.     // Returns true or false depending upon whether data should be
  768.     // automatically retrieved for the passed accessor.
  769.     bool IsAutoAccessor(ULONG nAccessor) const
  770.     {
  771.         ATLASSERT(nAccessor < m_nAccessors);
  772.         ATLASSERT(m_pAccessorInfo != NULL);
  773.         return m_pAccessorInfo[nAccessor].bAutoAccessor;
  774.     }
  775.  
  776. // Implementation
  777.     // Used by the rowset class to find out where to place the data
  778.     BYTE* GetBuffer() const
  779.     {
  780.         return m_pBuffer;
  781.     }
  782.     // Set the buffer that is used to retrieve the data
  783.     void SetBuffer(BYTE* pBuffer)
  784.     {
  785.         m_pBuffer = pBuffer;
  786.     }
  787.  
  788.     // Allocate internal memory for the passed number of accessors
  789.     HRESULT AllocateAccessorMemory(int nAccessors)
  790.     {
  791.         // Can't be called twice without calling ReleaseAccessors first
  792.         ATLASSERT(m_pAccessorInfo == NULL);
  793.         m_nAccessors    = nAccessors;
  794.         m_pAccessorInfo = NULL;
  795.         ATLTRY(m_pAccessorInfo = new _ATL_ACCESSOR_INFO[nAccessors]);
  796.         if (m_pAccessorInfo == NULL)
  797.             return E_OUTOFMEMORY;
  798.         else
  799.             return S_OK;
  800.     }
  801.     // BindParameters will be overriden if parameters are used
  802.     HRESULT BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }
  803.  
  804.     // Create an accessor for the passed binding information. The created accessor is
  805.     // returned through the pHAccessor parameter.
  806.     static HRESULT BindEntries(DBBINDING* pBindings, int nColumns, HACCESSOR* pHAccessor,
  807.         ULONG nSize, IAccessor* pAccessor)
  808.     {
  809.         ATLASSERT(pBindings  != NULL);
  810.         ATLASSERT(pHAccessor != NULL);
  811.         ATLASSERT(pAccessor  != NULL);
  812.         HRESULT hr;
  813.         int i;
  814.         DWORD dwAccessorFlags = (pBindings->eParamIO == DBPARAMIO_NOTPARAM) ?
  815.             DBACCESSOR_ROWDATA : DBACCESSOR_PARAMETERDATA;
  816.  
  817. #ifdef _DEBUG
  818.         // In debug builds we will retrieve the status flags and trace out
  819.         // any errors that may occur.
  820.         DBBINDSTATUS* pStatus = NULL;
  821.         ATLTRY(pStatus = new DBBINDSTATUS[nColumns]);
  822.         hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
  823.             pBindings, nSize, pHAccessor, pStatus);
  824.         if (FAILED(hr) && pStatus != NULL)
  825.         {
  826.             for (i=0; i<nColumns; i++)
  827.             {
  828.                 if (pStatus[i] != DBBINDSTATUS_OK)
  829.                     ATLTRACE2(atlTraceDBClient, 0, _T("Binding entry %d failed. Status: %d\n"), i, pStatus[i]);
  830.             }
  831.         }
  832.         delete [] pStatus;
  833. #else
  834.         hr = pAccessor->CreateAccessor(dwAccessorFlags, nColumns,
  835.             pBindings, nSize, pHAccessor, NULL);
  836. #endif
  837.         for (i=0; i<nColumns; i++)
  838.             delete pBindings[i].pObject;
  839.  
  840.         return hr;
  841.     }
  842.     // Set up the binding structure pointed to by pBindings based upon
  843.     // the other passed parameters.
  844.     static void Bind(DBBINDING* pBinding, ULONG nOrdinal, DBTYPE wType,
  845.         ULONG nLength, BYTE nPrecision, BYTE nScale, DBPARAMIO eParamIO,
  846.         ULONG nDataOffset, ULONG nLengthOffset = NULL, ULONG nStatusOffset = NULL,
  847.         DBOBJECT* pdbobject = NULL)
  848.     {
  849.         ATLASSERT(pBinding != NULL);
  850.  
  851.         // If we are getting a pointer to the data then let the provider
  852.         // own the memory
  853.         if (wType & DBTYPE_BYREF)
  854.             pBinding->dwMemOwner = DBMEMOWNER_PROVIDEROWNED;
  855.         else
  856.             pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  857.  
  858.         pBinding->pObject   = pdbobject;
  859.  
  860.         pBinding->eParamIO        = eParamIO;
  861.         pBinding->iOrdinal        = nOrdinal;
  862.         pBinding->wType            = wType;
  863.         pBinding->bPrecision    = nPrecision;
  864.         pBinding->bScale        = nScale;
  865.         pBinding->dwFlags        = 0;
  866.  
  867.         pBinding->obValue        = nDataOffset;
  868.         pBinding->obLength        = 0;
  869.         pBinding->obStatus        = 0;
  870.         pBinding->pTypeInfo        = NULL;
  871.         pBinding->pBindExt        = NULL;
  872.         pBinding->cbMaxLen        = nLength;
  873.  
  874.         pBinding->dwPart = DBPART_VALUE;
  875.         if (nLengthOffset != NULL)
  876.         {
  877.             pBinding->dwPart |= DBPART_LENGTH;
  878.             pBinding->obLength = nLengthOffset;
  879.         }
  880.         if (nStatusOffset != NULL)
  881.         {
  882.             pBinding->dwPart |= DBPART_STATUS;
  883.             pBinding->obStatus = nStatusOffset;
  884.         }
  885.     }
  886.  
  887.     // Free memory if appropriate
  888.     static inline void FreeType(DBTYPE wType, BYTE* pValue)
  889.     {
  890.         switch (wType)
  891.         {
  892.             case DBTYPE_BSTR:
  893.                 SysFreeString(*((BSTR*)pValue));
  894.             break;
  895.             case DBTYPE_VARIANT:
  896.                 VariantClear((VARIANT*)pValue);
  897.             break;
  898.             case DBTYPE_IUNKNOWN:
  899.             case DBTYPE_IDISPATCH:
  900.                 (*(IUnknown**)pValue)->Release();
  901.             break;
  902.             case DBTYPE_ARRAY:
  903.                 SafeArrayDestroy((SAFEARRAY*)pValue);
  904.             break;
  905.         }
  906.         if ((wType & DBTYPE_VECTOR) && ~(wType & DBTYPE_BYREF))
  907.             CoTaskMemFree(((DBVECTOR*)pValue)->ptr);
  908.     }
  909.  
  910.     _ATL_ACCESSOR_INFO* m_pAccessorInfo;
  911.     ULONG                m_nAccessors;
  912.     BYTE*               m_pBuffer;
  913. };
  914.  
  915. ///////////////////////////////////////////////////////////////////////////
  916. // class CRowset
  917.  
  918. class CRowset
  919. {
  920. // Constructors and Destructors
  921. public:
  922.     CRowset()
  923.     {
  924.         m_pAccessor = NULL;
  925.         m_hRow      = NULL;
  926.     }
  927.     CRowset(IRowset* pRowset)
  928.     {
  929.         m_spRowset    = pRowset;
  930.         CRowset();
  931.     }
  932.     ~CRowset()
  933.     {
  934.         Close();
  935.     }
  936.     // Release any retrieved row handles and then release the rowset
  937.     void Close()
  938.     {
  939.         if (m_spRowset != NULL)
  940.         {
  941.             ReleaseRows();
  942.             m_spRowset.Release();
  943.             m_spRowsetChange.Release();
  944.         }
  945.     }
  946.     // Addref the current row
  947.     HRESULT AddRefRows()
  948.     {
  949.         ATLASSERT(m_spRowset != NULL);
  950.         return m_spRowset->AddRefRows(1, &m_hRow, NULL, NULL);
  951.     }
  952.     // Release the current row
  953.     HRESULT ReleaseRows()
  954.     {
  955.         ATLASSERT(m_spRowset != NULL);
  956.         HRESULT hr = S_OK;
  957.  
  958.         if (m_hRow != NULL)
  959.         {
  960.             hr = m_spRowset->ReleaseRows(1, &m_hRow, NULL, NULL, NULL);
  961.             m_hRow = NULL;
  962.         }
  963.         return hr;
  964.     }
  965.     // Compare two bookmarks with each other
  966.     HRESULT Compare(const CBookmarkBase& bookmark1, const CBookmarkBase& bookmark2,
  967.         DBCOMPARE* pComparison) const
  968.     {
  969.         ATLASSERT(m_spRowset != NULL);
  970.         CComPtr<IRowsetLocate> spLocate;
  971.         HRESULT hr = m_spRowset.QueryInterface(&spLocate);
  972.         if (FAILED(hr))
  973.             return hr;
  974.  
  975.         return spLocate->Compare(NULL, bookmark1.GetSize(), bookmark1.GetBuffer(),
  976.             bookmark2.GetSize(), bookmark2.GetBuffer(), pComparison);
  977.     }
  978.     // Compare the passed hRow with the current row
  979.     HRESULT IsSameRow(HROW hRow) const
  980.     {
  981.         ATLASSERT(m_spRowset != NULL);
  982.         CComPtr<IRowsetIdentity> spRowsetIdentity;
  983.         HRESULT hr = m_spRowset.QueryInterface(&spRowsetIdentity);
  984.         if (FAILED(hr))
  985.             return hr;
  986.  
  987.         return spRowsetIdentity->IsSameRow(m_hRow, hRow);
  988.     }
  989.     // Move to the previous record
  990.     HRESULT MovePrev()
  991.     {
  992.         return MoveNext(-2, true);
  993.     }
  994.     // Move to the next record
  995.     HRESULT MoveNext()
  996.     {
  997.         return MoveNext(0, true);
  998.     }
  999.     // Move lSkip records forward or backward
  1000.     HRESULT MoveNext(LONG lSkip, bool bForward)
  1001.     {
  1002.         HRESULT hr;
  1003.         ULONG ulRowsFetched = 0;
  1004.  
  1005.         // Check the data was opened successfully and the accessor
  1006.         // has been set.
  1007.         ATLASSERT(m_spRowset != NULL);
  1008.         ATLASSERT(m_pAccessor != NULL);
  1009.  
  1010.         // Release a row if one is already around
  1011.         ReleaseRows();
  1012.  
  1013.         // Get the row handle
  1014.         HROW* phRow = &m_hRow;
  1015.         hr = m_spRowset->GetNextRows(NULL, lSkip, (bForward) ? 1 : -1, &ulRowsFetched, &phRow);
  1016.         if (hr != S_OK)
  1017.             return hr;
  1018.  
  1019.         // Get the data
  1020.         hr = GetData();
  1021.         if (FAILED(hr))
  1022.         {
  1023.             ATLTRACE2(atlTraceDBClient, 0, _T("GetData failed - HRESULT = 0x%X\n"),hr);
  1024.             ReleaseRows();
  1025.         }
  1026.         return hr;
  1027.     }
  1028.     // Move to the first record
  1029.     HRESULT MoveFirst()
  1030.     {
  1031.         HRESULT hr;
  1032.  
  1033.         // Check the data was opened successfully and the accessor
  1034.         // has been set.
  1035.         ATLASSERT(m_spRowset != NULL);
  1036.         ATLASSERT(m_pAccessor != NULL);
  1037.  
  1038.         // Release a row if one is already around
  1039.         ReleaseRows();
  1040.  
  1041.         hr = m_spRowset->RestartPosition(NULL);
  1042.         if (FAILED(hr))
  1043.             return hr;
  1044.  
  1045.         // Get the data
  1046.         return MoveNext();
  1047.     }
  1048.     // Move to the last record
  1049.     HRESULT MoveLast()
  1050.     {
  1051.         return MoveToRatio(1, 1, false);
  1052.     }
  1053.     // Move to the passed bookmark
  1054.     HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
  1055.     {
  1056.         // Check the data was opened successfully and the accessor
  1057.         // has been set.
  1058.         ATLASSERT(m_spRowset != NULL);
  1059.         ATLASSERT(m_pAccessor != NULL);
  1060.  
  1061.         CComPtr<IRowsetLocate> spLocate;
  1062.         HRESULT hr = m_spRowset.QueryInterface(&spLocate);
  1063.         if (FAILED(hr))
  1064.             return hr;
  1065.  
  1066.         // Release a row if one is already around
  1067.         ReleaseRows();
  1068.  
  1069.         ULONG ulRowsFetched = 0;
  1070.         HROW* phRow = &m_hRow;
  1071.         hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
  1072.             lSkip, 1, &ulRowsFetched, &phRow);
  1073.         // Note we're not using SUCCEEDED here, because we could get DB_S_ENDOFROWSET
  1074.         if (hr != S_OK)
  1075.             return hr;
  1076.  
  1077.         // Get the data
  1078.         hr = GetData();
  1079.         if (FAILED(hr))
  1080.         {
  1081.             ATLTRACE2(atlTraceDBClient, 0, _T("GetData from Bookmark failed - HRESULT = 0x%X\n"),hr);
  1082.             ReleaseRows();
  1083.         }
  1084.  
  1085.         return S_OK;
  1086.     }
  1087.     // Get the data for the current record
  1088.     HRESULT GetData()
  1089.     {
  1090.         HRESULT hr = S_OK;
  1091.         ATLASSERT(m_pAccessor != NULL);
  1092.  
  1093.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1094.         for (ULONG i=0; i<nAccessors; i++)
  1095.         {
  1096.             if (m_pAccessor->IsAutoAccessor(i))
  1097.             {
  1098.                 hr = GetData(i);
  1099.                 if (FAILED(hr))
  1100.                     return hr;
  1101.             }
  1102.         }
  1103.         return hr;
  1104.     }
  1105.     // Get the data for the passed accessor. Use for a non-auto accessor
  1106.     HRESULT GetData(int nAccessor)
  1107.     {
  1108.         ATLASSERT(m_spRowset != NULL);
  1109.         ATLASSERT(m_pAccessor != NULL);
  1110.         ATLASSERT(m_hRow != NULL);
  1111.  
  1112.         // Note that we are using the specified buffer if it has been set,
  1113.         // otherwise we use the accessor for the data.
  1114.         return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), m_pAccessor->GetBuffer());
  1115.     }
  1116.     // Get the data for the passed accessor. Use for a non-auto accessor
  1117.     HRESULT GetDataHere(int nAccessor, void* pBuffer)
  1118.     {
  1119.         ATLASSERT(m_spRowset != NULL);
  1120.         ATLASSERT(m_pAccessor != NULL);
  1121.         ATLASSERT(m_hRow != NULL);
  1122.  
  1123.         // Note that we are using the specified buffer if it has been set,
  1124.         // otherwise we use the accessor for the data.
  1125.         return m_spRowset->GetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor), pBuffer);
  1126.     }
  1127.     HRESULT GetDataHere(void* pBuffer)
  1128.     {
  1129.         HRESULT hr = S_OK;
  1130.  
  1131.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1132.         for (ULONG i=0; i<nAccessors; i++)
  1133.         {
  1134.             hr = GetDataHere(i, pBuffer);
  1135.             if (FAILED(hr))
  1136.                 return hr;
  1137.         }
  1138.         return hr;
  1139.     }
  1140.  
  1141.     // Insert the current record
  1142.     HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
  1143.     {
  1144.         ATLASSERT(m_pAccessor != NULL);
  1145.         HRESULT hr;
  1146.         if (m_spRowsetChange != NULL)
  1147.         {
  1148.             HROW* pHRow;
  1149.             if (bGetHRow)
  1150.             {
  1151.                 ReleaseRows();
  1152.                 pHRow = &m_hRow;
  1153.             }
  1154.             else
  1155.                 pHRow = NULL;
  1156.  
  1157.             hr = m_spRowsetChange->InsertRow(NULL, m_pAccessor->GetHAccessor(nAccessor),
  1158.                     m_pAccessor->GetBuffer(), pHRow);
  1159.             
  1160.         }
  1161.         else
  1162.             hr = E_NOINTERFACE;
  1163.     
  1164.         return hr;
  1165.     }
  1166.     // Delete the current record
  1167.     HRESULT Delete() const
  1168.     {
  1169.         ATLASSERT(m_pAccessor != NULL);
  1170.         HRESULT hr;
  1171.         if (m_spRowsetChange != NULL)
  1172.             hr = m_spRowsetChange->DeleteRows(NULL, 1, &m_hRow, NULL);
  1173.         else
  1174.             hr = E_NOINTERFACE;
  1175.  
  1176.         return hr;
  1177.     }
  1178.     // Update the current record
  1179.     HRESULT SetData() const
  1180.     {
  1181.         ATLASSERT(m_pAccessor != NULL);
  1182.         HRESULT hr = S_OK;
  1183.  
  1184.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1185.         for (ULONG i=0; i<nAccessors; i++)
  1186.         {
  1187.             hr = SetData(i);
  1188.             if (FAILED(hr))
  1189.                 return hr;
  1190.         }
  1191.         return hr;
  1192.     }
  1193.     // Update the current record with the data in the passed accessor
  1194.     HRESULT SetData(int nAccessor) const
  1195.     {
  1196.         ATLASSERT(m_pAccessor != NULL);
  1197.         HRESULT hr;
  1198.         if (m_spRowsetChange != NULL)
  1199.         {
  1200.             hr = m_spRowsetChange->SetData(m_hRow, m_pAccessor->GetHAccessor(nAccessor),
  1201.                 m_pAccessor->GetBuffer());
  1202.         }
  1203.         else
  1204.             hr = E_NOINTERFACE;
  1205.  
  1206.         return hr;
  1207.     }
  1208.  
  1209.     // Get the data most recently fetched from or transmitted to the data source.
  1210.     // Does not get values based on pending changes.
  1211.     HRESULT GetOriginalData()
  1212.     {
  1213.         ATLASSERT(m_spRowset != NULL);
  1214.         ATLASSERT(m_pAccessor != NULL);
  1215.  
  1216.         HRESULT hr = S_OK;
  1217.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1218.         hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1219.         if (FAILED(hr))
  1220.             return hr;
  1221.  
  1222.         ULONG nAccessors = m_pAccessor->GetNumAccessors();
  1223.         for (ULONG i = 0; i < nAccessors; i++)
  1224.         {
  1225.             hr = spRowsetUpdate->GetOriginalData(m_hRow, m_pAccessor->GetHAccessor(i), m_pAccessor->GetBuffer());
  1226.             if (FAILED(hr))
  1227.                 return hr;
  1228.         }
  1229.         return hr;
  1230.     }
  1231.     // Get the status of the current row
  1232.     HRESULT GetRowStatus(DBPENDINGSTATUS* pStatus) const
  1233.     {
  1234.         ATLASSERT(m_spRowset != NULL);
  1235.         ATLASSERT(pStatus != NULL);
  1236.  
  1237.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1238.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1239.         if (FAILED(hr))
  1240.             return hr;
  1241.  
  1242.         return spRowsetUpdate->GetRowStatus(NULL, 1, &m_hRow, pStatus);
  1243.     }
  1244.     // Undo any changes made to the current row since it was last fetched or Update
  1245.     // was called for it
  1246.     HRESULT Undo(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
  1247.     {
  1248.         ATLASSERT(m_spRowset != NULL);
  1249.  
  1250.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1251.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1252.         if (FAILED(hr))
  1253.             return hr;
  1254.  
  1255.         HROW*           prgRows;
  1256.         DBROWSTATUS*    pRowStatus;
  1257.         if (phRow != NULL)
  1258.             hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
  1259.         else
  1260.             hr = spRowsetUpdate->Undo(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
  1261.         if (FAILED(hr))
  1262.             return hr;
  1263.  
  1264.         if (phRow != NULL)
  1265.         {
  1266.             *phRow = *prgRows;
  1267.             CoTaskMemFree(prgRows);
  1268.         }
  1269.         if (pStatus != NULL)
  1270.             *pStatus = *pRowStatus;
  1271.  
  1272.         CoTaskMemFree(pRowStatus);
  1273.         return hr;
  1274.     }
  1275.     // Transmits any pending changes made to a row since it was last fetched or Update was
  1276.     // called for it. Also see SetData.
  1277.     HRESULT Update(ULONG* pcRows = NULL, HROW* phRow = NULL, DBROWSTATUS* pStatus = NULL)
  1278.     {
  1279.         ATLASSERT(m_spRowset != NULL);
  1280.  
  1281.         CComPtr<IRowsetUpdate> spRowsetUpdate;
  1282.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetUpdate);
  1283.         if (FAILED(hr))
  1284.             return hr;
  1285.  
  1286.         HROW*           prgRows;
  1287.         DBROWSTATUS*    pRowStatus;
  1288.         if (phRow != NULL)
  1289.             hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, &prgRows, &pRowStatus);
  1290.         else
  1291.             hr = spRowsetUpdate->Update(NULL, 1, &m_hRow, pcRows, NULL, &pRowStatus);
  1292.         if (FAILED(hr))
  1293.             return hr;
  1294.  
  1295.         if (phRow != NULL)
  1296.         {
  1297.             *phRow = *prgRows;
  1298.             CoTaskMemFree(prgRows);
  1299.         }
  1300.         if (pStatus != NULL)
  1301.             *pStatus = *pRowStatus;
  1302.  
  1303.         CoTaskMemFree(pRowStatus);
  1304.         return hr;
  1305.     }
  1306.  
  1307.     // Get the approximate position of the row corresponding to the passed bookmark
  1308.     HRESULT GetApproximatePosition(const CBookmarkBase* pBookmark, ULONG* pPosition, ULONG* pcRows)
  1309.     {
  1310.         ATLASSERT(m_spRowset != NULL);
  1311.  
  1312.         CComPtr<IRowsetScroll> spRowsetScroll;
  1313.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1314.         if (SUCCEEDED(hr))
  1315.         {
  1316.             if (pBookmark != NULL)
  1317.                 hr = spRowsetScroll->GetApproximatePosition(NULL, pBookmark->GetSize(), pBookmark->GetBuffer(),
  1318.                         pPosition, pcRows);
  1319.             else
  1320.                 hr = spRowsetScroll->GetApproximatePosition(NULL, 0, NULL, pPosition, pcRows);
  1321.  
  1322.         }
  1323.         return hr;
  1324.  
  1325.     }
  1326.     // Move to a fractional position in the rowset
  1327.     HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator, bool bForward = true)
  1328.     {
  1329.         ATLASSERT(m_spRowset != NULL);
  1330.         ULONG   nRowsFetched;
  1331.  
  1332.         CComPtr<IRowsetScroll> spRowsetScroll;
  1333.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1334.         if (FAILED(hr))
  1335.             return hr;
  1336.  
  1337.         ReleaseRows();
  1338.         HROW* phRow = &m_hRow;
  1339.         hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, (bForward) ? 1 : -1,
  1340.             &nRowsFetched, &phRow);
  1341.         if (SUCCEEDED(hr))
  1342.             hr = GetData();
  1343.  
  1344.         return hr;
  1345.     }
  1346.  
  1347. // Implementation
  1348.     static const IID& GetRowsetIID()
  1349.     {
  1350.         return IID_IRowset;
  1351.     }
  1352.     IRowset* GetIRowset() const
  1353.     {
  1354.         return m_spRowset;
  1355.     }
  1356.     IRowset** GetIRowsetPtr()
  1357.     {
  1358.         return &m_spRowset;
  1359.     }
  1360.     void SetupOptionalRowsetInterfaces()
  1361.     {
  1362.         // Cache IRowsetChange if available
  1363.         m_spRowset->QueryInterface(&m_spRowsetChange);
  1364.     }
  1365.     HRESULT BindFinished() const { return S_OK; }
  1366.     void    SetAccessor(CAccessorBase* pAccessor)
  1367.     {
  1368.         m_pAccessor = pAccessor;
  1369.     }
  1370.  
  1371.     CComPtr<IRowset>        m_spRowset;
  1372.     CComPtr<IRowsetChange>    m_spRowsetChange;
  1373.     CAccessorBase*            m_pAccessor;
  1374.     HROW                    m_hRow;
  1375. };
  1376.  
  1377.  
  1378. ///////////////////////////////////////////////////////////////////////////
  1379. // class CBulkRowset
  1380.  
  1381. class CBulkRowset : public CRowset
  1382. {
  1383. public:
  1384.     CBulkRowset()
  1385.     {
  1386.         // Default the number of rows to bulk fetch to 10
  1387.         m_nRows = 10;
  1388.         m_hr    = S_OK;
  1389.         m_phRow = NULL;
  1390.     }
  1391.     CBulkRowset::~CBulkRowset()
  1392.     {
  1393.         Close();
  1394.  
  1395.         delete [] m_phRow;
  1396.     }
  1397.     // Set the number of row handles that will be retrieved in each
  1398.     // bulk row fetch. The default is 10 and this function must be called
  1399.     // before Open if you wish to change it.
  1400.     void SetRows(ULONG nRows)
  1401.     {
  1402.         // This function must be called before the memory is allocated
  1403.         // during binding
  1404.         ATLASSERT(m_phRow == NULL);
  1405.         m_nRows = nRows;
  1406.     }
  1407.     // AddRef all the currently retrieved row handles
  1408.     HRESULT AddRefRows()
  1409.     {
  1410.         ATLASSERT(m_spRowset != NULL);
  1411.         return m_spRowset->AddRefRows(m_nCurrentRows, m_phRow, NULL, NULL);
  1412.     }
  1413.     // Release all the currently retrieved row handles
  1414.     HRESULT ReleaseRows()
  1415.     {
  1416.         ATLASSERT(m_spRowset != NULL);
  1417.         // We're going to Release the rows so reset the current row position
  1418.         m_nCurrentRow = 0;
  1419.         m_hRow        = NULL;
  1420.         return m_spRowset->ReleaseRows(m_nCurrentRows, m_phRow, NULL, NULL, NULL);
  1421.     }
  1422.     // Move to the first record
  1423.     HRESULT MoveFirst()
  1424.     {
  1425.         ATLASSERT(m_spRowset != NULL);
  1426.         ReleaseRows();
  1427.  
  1428.         // Cause MoveNext to perform a new bulk fetch
  1429.         m_nCurrentRow  = m_nRows;
  1430.  
  1431.         HRESULT hr = m_spRowset->RestartPosition(NULL);
  1432.         if (FAILED(hr))
  1433.             return hr;
  1434.  
  1435.         // Get the data
  1436.         return MoveNext();
  1437.     }
  1438.     // Move to the next record
  1439.     HRESULT MoveNext()
  1440.     {
  1441.         ATLASSERT(m_spRowset != NULL);
  1442.         ATLASSERT(m_phRow    != NULL);
  1443.  
  1444.         // Move to the next record in the buffer
  1445.         m_nCurrentRow++;
  1446.  
  1447.         // Have we reached the end of the buffer?
  1448.         if (m_nCurrentRow >= m_nCurrentRows)
  1449.         {
  1450.             // If we've reached the end of the buffer and we had a non S_OK HRESULT from
  1451.             // the last call to GetNextRows then return that HRESULT now.
  1452.             if (m_hr != S_OK)
  1453.                 return m_hr;
  1454.  
  1455.             // We've finished with these rows so we need some more
  1456.             // First release any HROWs that we have
  1457.             ReleaseRows();
  1458.  
  1459.             m_hr = m_spRowset->GetNextRows(NULL, 0, m_nRows, &m_nCurrentRows, &m_phRow);
  1460.             // If we have an error HRESULT or we haven't retrieved any rows then return
  1461.             // the HRESULT now.
  1462.             if (FAILED(m_hr) || m_nCurrentRows == 0)
  1463.                 return m_hr;
  1464.         }
  1465.  
  1466.         // Get the data for the current row
  1467.         m_hRow = m_phRow[m_nCurrentRow];
  1468.         return GetData();
  1469.     }
  1470.     // Move to the previous record
  1471.     HRESULT MovePrev()
  1472.     {
  1473.         ATLASSERT(m_spRowset != NULL);
  1474.         ATLASSERT(m_phRow    != NULL);
  1475.  
  1476.         // Check if we're at the start of the block
  1477.         if (m_nCurrentRow == 0)
  1478.         {
  1479.             ReleaseRows();
  1480.  
  1481.             // Go back double the amount of rows in the block and fetch forward
  1482.             m_hr = m_spRowset->GetNextRows(NULL, -(LONG)m_nRows * 2, m_nRows, &m_nCurrentRows, &m_phRow);
  1483.  
  1484.             // Set the current record to the end of the new block
  1485.             m_nCurrentRow = m_nCurrentRows - 1;
  1486.  
  1487.             // If we have an error HRESULT or we haven't retrieved any rows then return
  1488.             // the HRESULT now.
  1489.             if (FAILED(m_hr) || m_nCurrentRows == 0)
  1490.                 return m_hr;
  1491.         }
  1492.         else
  1493.         {
  1494.             // Move back a row in the block
  1495.             m_nCurrentRow--;
  1496.         }
  1497.  
  1498.         // Get the data for the current row
  1499.         m_hRow = m_phRow[m_nCurrentRow];
  1500.         return GetData();
  1501.     }
  1502.     // Move to the last record
  1503.     HRESULT MoveLast()
  1504.     {
  1505.         HRESULT hr;
  1506.         hr = MoveToRatio(1, 1);    // Move to the end
  1507.         if (SUCCEEDED(hr))
  1508.             hr = MovePrev();    // and then get the last record
  1509.         return hr;
  1510.     }
  1511.     // Move to the passed bookmark
  1512.     HRESULT MoveToBookmark(const CBookmarkBase& bookmark, LONG lSkip = 0)
  1513.     {
  1514.         ATLASSERT(m_spRowset != NULL);
  1515.         CComPtr<IRowsetLocate> spLocate;
  1516.         HRESULT hr = m_spRowset->QueryInterface(&spLocate);
  1517.         if (FAILED(hr))
  1518.             return hr;
  1519.  
  1520.         ReleaseRows();
  1521.         m_hr = spLocate->GetRowsAt(NULL, NULL, bookmark.GetSize(), bookmark.GetBuffer(),
  1522.             lSkip, m_nRows, &m_nCurrentRows, &m_phRow);
  1523.         if (FAILED(m_hr) || m_nCurrentRows == 0)
  1524.             return m_hr;
  1525.  
  1526.         // Get the data
  1527.         m_hRow = m_phRow[m_nCurrentRow];
  1528.         return GetData();
  1529.     }
  1530.     // Move to a fractional position in the rowset
  1531.     HRESULT MoveToRatio(ULONG nNumerator, ULONG nDenominator)
  1532.     {
  1533.         ATLASSERT(m_spRowset != NULL);
  1534.  
  1535.         CComPtr<IRowsetScroll> spRowsetScroll;
  1536.         HRESULT hr = m_spRowset->QueryInterface(&spRowsetScroll);
  1537.         if (FAILED(hr))
  1538.             return hr;
  1539.  
  1540.         ReleaseRows();
  1541.         m_hr = spRowsetScroll->GetRowsAtRatio(NULL, NULL, nNumerator, nDenominator, m_nRows, &m_nCurrentRows, &m_phRow);
  1542.         if (FAILED(m_hr) || m_nCurrentRows == 0)
  1543.             return m_hr;
  1544.  
  1545.         // Get the data
  1546.         m_hRow = m_phRow[m_nCurrentRow];
  1547.         return GetData();
  1548.     }
  1549.     // Insert the current record
  1550.     HRESULT Insert(int nAccessor = 0, bool bGetHRow = false)
  1551.     {
  1552.         ReleaseRows();
  1553.         return CRowset::Insert(nAccessor, bGetHRow);
  1554.     }
  1555.  
  1556. // Implementation
  1557.     HRESULT BindFinished()
  1558.     {
  1559.         // No rows in the buffer yet
  1560.         m_nCurrentRows = 0;
  1561.         // Cause MoveNext to automatically perform a new bulk fetch the first time
  1562.         m_nCurrentRow  = m_nRows;
  1563.  
  1564.         m_phRow = NULL;
  1565.         ATLTRY(m_phRow = new HROW[m_nRows]);
  1566.         if (m_phRow == NULL)
  1567.             return E_OUTOFMEMORY;
  1568.  
  1569.         return S_OK;
  1570.     }
  1571.  
  1572.     HRESULT m_hr;           // HRESULT to return from MoveNext at end of buffer
  1573.     HROW*   m_phRow;        // Pointer to array of HROWs for each row in buffer
  1574.     ULONG   m_nRows;        // Number of rows that will fit in the buffer
  1575.     ULONG   m_nCurrentRows; // Number of rows currently in the buffer
  1576.     ULONG   m_nCurrentRow;
  1577. };
  1578.  
  1579. ///////////////////////////////////////////////////////////////////////////
  1580. // class CArrayRowset
  1581. //
  1582. // Allows you to access a rowset with an array syntax
  1583.  
  1584. template <class T, class TRowset = CRowset>
  1585. class CArrayRowset :
  1586.     public CVirtualBuffer<T>,
  1587.     public TRowset
  1588. {
  1589. public:
  1590.     CArrayRowset(int nMax = 100000) : CVirtualBuffer<T>(nMax) 
  1591.     {
  1592.         m_nRowsRead = 0;
  1593.     }
  1594.     T& operator[](int nRow)
  1595.     {
  1596.         ATLASSERT(nRow >= 0);
  1597.         HRESULT hr;
  1598.         T* m_pCurrent = m_pBase + m_nRowsRead;
  1599.  
  1600.         // Retrieve the row if we haven't retrieved it already
  1601.         while ((ULONG)nRow >= m_nRowsRead)
  1602.         {
  1603.             m_pAccessor->SetBuffer((BYTE*)m_pCurrent);
  1604.             __try
  1605.             {
  1606.                 // Get the row
  1607.                 hr = MoveNext();
  1608.                 if (hr != S_OK)
  1609.                 {
  1610.                     *((char*)0) = 0; // Force exception
  1611.                 }
  1612.             }
  1613.             __except(Except(GetExceptionInformation()))
  1614.             {
  1615.             }
  1616.             m_nRowsRead++;
  1617.             m_pCurrent++;
  1618.         }
  1619.  
  1620.         return *(m_pBase + nRow);
  1621.     }
  1622.  
  1623.     HRESULT Snapshot()
  1624.     {
  1625.         ATLASSERT(m_nRowsRead == 0);
  1626.         ATLASSERT(m_spRowset != NULL);
  1627.         HRESULT hr = MoveFirst();
  1628.         if (FAILED(hr))
  1629.             return hr;
  1630.         do
  1631.         {
  1632.             Write(*(T*)m_pAccessor->GetBuffer());
  1633.             m_nRowsRead++;
  1634.             hr = MoveNext();
  1635.         } while (SUCCEEDED(hr) &&  hr != DB_S_ENDOFROWSET);
  1636.         
  1637.         return (hr == DB_S_ENDOFROWSET) ? S_OK : hr;
  1638.     }
  1639.  
  1640.  
  1641. // Implementation
  1642.     ULONG   m_nRowsRead;
  1643. };
  1644.  
  1645. // Used when you don't need any parameters or output columns
  1646. class CNoAccessor
  1647. {
  1648. public:
  1649.     // We don't need any typedef's here as the default
  1650.     // global typedef is not to have any parameters and
  1651.     // output columns.
  1652.     HRESULT BindColumns(IUnknown*) { return S_OK; }
  1653.     HRESULT    BindParameters(HACCESSOR*, ICommand*, void**) { return S_OK; }
  1654.     void    Close() { }
  1655.     HRESULT ReleaseAccessors(IUnknown*) { return S_OK; }
  1656. };
  1657.  
  1658. // Used when a rowset will not be returned from the command
  1659. class CNoRowset
  1660. {
  1661. public:
  1662.     HRESULT                BindFinished() { return S_OK; }
  1663.     void                Close() { }
  1664.     static const IID&    GetRowsetIID() { return IID_NULL; }
  1665.     IRowset*            GetIRowset() const { return NULL; }
  1666.     IRowset**            GetIRowsetPtr() { return NULL; }
  1667.     void                SetAccessor(void*) { }
  1668.     void                SetupOptionalRowsetInterfaces() { }
  1669. };
  1670.  
  1671. ///////////////////////////////////////////////////////////////////////////
  1672. // class CAccessor
  1673.  
  1674. // T is the class that contains the data that will be accessed.
  1675. template <class T>
  1676. class CAccessor :
  1677.     public T,
  1678.     public CAccessorBase
  1679. {
  1680. public:
  1681.     // Free's any columns in the current record that need to be freed.
  1682.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  1683.     void FreeRecordMemory()
  1684.     {
  1685.         ULONG nColumns;
  1686.         ULONG i;
  1687.  
  1688.         for (i = 0; i < GetNumAccessors(); i++)
  1689.         {
  1690.             // Passing in m_pBuffer tells the column entry maps to free the
  1691.             // memory for the types if appropriate
  1692.             _GetBindEntries(&nColumns, NULL, i, NULL, m_pBuffer);
  1693.         }
  1694.     }
  1695. // Implementation
  1696.     HRESULT BindColumns(IUnknown* pUnk)
  1697.     {
  1698.         HRESULT hr;
  1699.         ULONG    nAccessors;
  1700.         ULONG    nSize;
  1701.         nAccessors = _OutputColumnsClass::_GetNumAccessors();
  1702.  
  1703.         SetBuffer((BYTE*)this);
  1704.  
  1705.         // Don't add the internal memory we use to the size of the data
  1706.         // buffer we pass to the provider
  1707.         nSize = sizeof(T) - sizeof(CAccessor<T>);
  1708.         hr = BindAccessors(nAccessors, nSize, pUnk);
  1709.         return hr;
  1710.     }
  1711.     HRESULT BindAccessors(ULONG nAccessors, ULONG nSize, IUnknown* pUnk)
  1712.     {
  1713.         ATLASSERT(pUnk != NULL);
  1714.         HRESULT hr;
  1715.  
  1716.         CComPtr<IAccessor> spAccessor;
  1717.         hr = pUnk->QueryInterface(&spAccessor);
  1718.         if (SUCCEEDED(hr))
  1719.         {
  1720.             // Allocate the accessor memory if we haven't done so yet
  1721.             if (m_pAccessorInfo == NULL)
  1722.             {
  1723.                 hr = AllocateAccessorMemory(nAccessors);
  1724.                 if (FAILED(hr))
  1725.                     return hr;
  1726.             }
  1727.  
  1728.             for (ULONG i=0; i<nAccessors && SUCCEEDED(hr); i++)
  1729.                 hr = BindAccessor(spAccessor, i, nSize);
  1730.         }
  1731.  
  1732.         return hr;
  1733.     }
  1734.  
  1735.     HRESULT BindAccessor(IAccessor* pAccessor, ULONG nAccessor, ULONG nSize)
  1736.     {
  1737.         DBBINDING*  pBindings = NULL;
  1738.         ULONG       nColumns;
  1739.         bool        bAuto;
  1740.         HRESULT        hr;
  1741.  
  1742.         // First time just get the number of entries by passing in &nColumns
  1743.         _OutputColumnsClass::_GetBindEntries(&nColumns, NULL, nAccessor, NULL);
  1744.  
  1745.         // Now allocate the binding structures
  1746.         ATLTRY(pBindings = new DBBINDING[nColumns]);
  1747.         if (pBindings == NULL)
  1748.             return E_OUTOFMEMORY;
  1749.  
  1750.         // Now get the bind entries
  1751.         hr = _OutputColumnsClass::_GetBindEntries(&nColumns, pBindings, nAccessor, &bAuto);
  1752.         if (FAILED(hr))
  1753.             return hr;
  1754.  
  1755.         m_pAccessorInfo[nAccessor].bAutoAccessor = bAuto;
  1756.         hr = BindEntries(pBindings, nColumns, &m_pAccessorInfo[nAccessor].hAccessor, nSize, pAccessor);
  1757.         delete [] pBindings;
  1758.         return hr;
  1759.     }
  1760.  
  1761.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand, void** ppParameterBuffer)
  1762.     {
  1763.         HRESULT hr = S_OK;
  1764.         // In the static accessor case, the parameter buffer will be T
  1765.         *ppParameterBuffer = this;
  1766.  
  1767.         // Only bind the parameters if we haven't already done it
  1768.         if (*pHAccessor == NULL)
  1769.         {
  1770.             ULONG   nColumns;
  1771.             _ParamClass::_GetParamEntries(&nColumns, NULL);
  1772.  
  1773.             DBBINDING* pBinding = NULL;
  1774.             ATLTRY(pBinding = new DBBINDING[nColumns]);
  1775.             if (pBinding == NULL)
  1776.                 return E_OUTOFMEMORY;
  1777.  
  1778.             hr = _ParamClass::_GetParamEntries(&nColumns, pBinding);
  1779.             if (SUCCEEDED(hr))
  1780.             {
  1781.                 // Get the IAccessor from the passed IUnknown
  1782.                 CComPtr<IAccessor> spAccessor;
  1783.                 hr = pCommand->QueryInterface(&spAccessor);
  1784.                 if (SUCCEEDED(hr))
  1785.                 {
  1786.                     hr = BindEntries(pBinding, nColumns, pHAccessor, sizeof(T) - sizeof(CAccessor<T>),
  1787.                         spAccessor);
  1788.                 }
  1789.             }
  1790.             delete [] pBinding;
  1791.         }
  1792.         return hr;
  1793.     }
  1794. };
  1795.  
  1796.  
  1797. ///////////////////////////////////////////////////////////////////////////
  1798. // CDynamicAccessor
  1799.  
  1800. class CDynamicAccessor :
  1801.     public CAccessorBase
  1802. {
  1803. public:
  1804.     CDynamicAccessor()
  1805.     {
  1806.         m_nColumns        = 0;
  1807.         m_pColumnInfo     = NULL;
  1808.         m_pStringsBuffer  = NULL;
  1809.     };
  1810.     ~CDynamicAccessor()
  1811.     {
  1812.         Close();
  1813.     }
  1814.     void Close()
  1815.     {
  1816.         if (m_pColumnInfo != NULL)
  1817.         {
  1818.             CoTaskMemFree(m_pColumnInfo);
  1819.             m_pColumnInfo = NULL;
  1820.         }
  1821.  
  1822.         // Free the memory for the string buffer returned by IColumnsInfo::GetColumnInfo,
  1823.         // if necessary
  1824.         if (m_pStringsBuffer != NULL)
  1825.         {
  1826.             CoTaskMemFree(m_pStringsBuffer);
  1827.             m_pStringsBuffer = NULL;
  1828.         }
  1829.  
  1830.         delete [] m_pBuffer;
  1831.         m_pBuffer = NULL;
  1832.         m_nColumns = 0;
  1833.  
  1834.         CAccessorBase::Close();
  1835.     }
  1836.     bool GetColumnType(ULONG nColumn, DBTYPE* pType) const
  1837.     {
  1838.         if (TranslateColumnNo(nColumn))
  1839.         {
  1840.             *pType = m_pColumnInfo[nColumn].wType;
  1841.             return true;
  1842.         }
  1843.         else
  1844.             return false;
  1845.     }
  1846.     bool GetColumnFlags(ULONG nColumn, DBCOLUMNFLAGS* pFlags) const
  1847.     {
  1848.         if (TranslateColumnNo(nColumn))
  1849.         {
  1850.             *pFlags = m_pColumnInfo[nColumn].dwFlags;
  1851.             return true;
  1852.         }
  1853.         else
  1854.             return false;
  1855.     }
  1856.     bool GetOrdinal(TCHAR* pColumnName, ULONG* pOrdinal) const
  1857.     {
  1858.         ULONG nColumn;
  1859.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1860.         {
  1861.             *pOrdinal = m_pColumnInfo[nColumn].iOrdinal;
  1862.             return true;
  1863.         }
  1864.         else
  1865.             return false;
  1866.     }
  1867.  
  1868.     void* GetValue(ULONG nColumn) const
  1869.     {
  1870.         if (TranslateColumnNo(nColumn))
  1871.             return _GetDataPtr(nColumn);
  1872.         else
  1873.             return NULL;
  1874.     }
  1875.  
  1876.     void* GetValue(TCHAR* pColumnName) const
  1877.     {
  1878.         ULONG nColumn;
  1879.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1880.             return _GetDataPtr(nColumn);
  1881.         else
  1882.             return NULL;    // Not Found
  1883.     }
  1884.  
  1885.     template <class ctype>
  1886.     void _GetValue(ULONG nColumn, ctype* pData) const
  1887.     {
  1888.         ATLASSERT(pData != NULL);
  1889.         ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
  1890.         ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
  1891.         *pData = *pBuffer;
  1892.     }
  1893.     template <class ctype>
  1894.     void _SetValue(ULONG nColumn, const ctype& data)
  1895.     {
  1896.         ATLASSERT(m_pColumnInfo[nColumn].ulColumnSize == sizeof(ctype));
  1897.         ctype* pBuffer = (ctype*)_GetDataPtr(nColumn);
  1898.         *pBuffer = (ctype)data;
  1899.     }
  1900.     template <class ctype>
  1901.     bool GetValue(ULONG nColumn, ctype* pData) const
  1902.     {
  1903.         if (TranslateColumnNo(nColumn))
  1904.         {
  1905.             _GetValue(nColumn, pData);
  1906.             return true;
  1907.         }
  1908.         return false;
  1909.     }
  1910.     template <class ctype>
  1911.     bool SetValue(ULONG nColumn, const ctype& data)
  1912.     {
  1913.         if (TranslateColumnNo(nColumn))
  1914.         {
  1915.             _SetValue(nColumn, data);
  1916.             return true;
  1917.         }
  1918.         return false;
  1919.     }
  1920.     template <class ctype>
  1921.     bool GetValue(TCHAR *pColumnName, ctype* pData) const
  1922.     {
  1923.         ULONG nColumn;
  1924.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1925.         {
  1926.             _GetValue(nColumn, pData);
  1927.             return true;
  1928.         }
  1929.         return false;
  1930.     }
  1931.     template <class ctype>
  1932.     bool SetValue(TCHAR *pColumnName, const ctype& data)
  1933.     {
  1934.         ULONG nColumn;
  1935.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1936.         {
  1937.             _SetValue(nColumn, data);
  1938.             return true;
  1939.         }
  1940.         return false;
  1941.     }
  1942.     bool GetLength(ULONG nColumn, ULONG* pLength) const
  1943.     {
  1944.         ATLASSERT(pLength != NULL);
  1945.         if (TranslateColumnNo(nColumn))
  1946.         {
  1947.             *pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
  1948.             return true;
  1949.         }
  1950.         else
  1951.             return false;
  1952.     }
  1953.     bool SetLength(ULONG nColumn, ULONG nLength)
  1954.     {
  1955.         if (TranslateColumnNo(nColumn))
  1956.         {
  1957.             *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
  1958.             return true;
  1959.         }
  1960.         else
  1961.             return false;
  1962.     }
  1963.     bool GetLength(TCHAR* pColumnName, ULONG* pLength) const
  1964.     {
  1965.         ATLASSERT(pLength != NULL);
  1966.         ULONG nColumn;
  1967.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1968.         {
  1969.             *pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
  1970.             return true;
  1971.         }
  1972.         else
  1973.             return false;
  1974.     }
  1975.     bool SetLength(TCHAR* pColumnName, ULONG nLength)
  1976.     {
  1977.         ULONG nColumn;
  1978.         if (GetInternalColumnNo(pColumnName, &nColumn))
  1979.         {
  1980.             *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
  1981.             return true;
  1982.         }
  1983.         else
  1984.             return false;
  1985.     }
  1986.     bool GetStatus(ULONG nColumn, DBSTATUS* pStatus) const
  1987.     {
  1988.         ATLASSERT(pStatus != NULL);
  1989.         if (TranslateColumnNo(nColumn))
  1990.         {
  1991.             *pStatus = *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG));
  1992.             return true;
  1993.         }
  1994.         else
  1995.             return false;
  1996.     }
  1997.     bool SetStatus(ULONG nColumn, DBSTATUS status)
  1998.     {
  1999.         if (TranslateColumnNo(nColumn))
  2000.         {
  2001.             *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG)) = status;
  2002.             return true;
  2003.         }
  2004.         else
  2005.             return false;
  2006.     }
  2007.     bool GetStatus(TCHAR* pColumnName, DBSTATUS* pStatus) const
  2008.     {
  2009.         ATLASSERT(pStatus != NULL);
  2010.         ULONG nColumn;
  2011.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2012.         {
  2013.             *pStatus = *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG));
  2014.             return true;
  2015.         }
  2016.         else
  2017.             return false;
  2018.     }
  2019.     bool SetStatus(TCHAR* pColumnName, DBSTATUS status)
  2020.     {
  2021.         ULONG nColumn;
  2022.         if (GetInternalColumnNo(pColumnName, &nColumn))
  2023.         {
  2024.             *(ULONG*)((BYTE*)_GetDataPtr(nColumn) + m_pColumnInfo[nColumn].ulColumnSize + sizeof(ULONG)) = status;
  2025.             return true;
  2026.         }
  2027.         else
  2028.             return false;
  2029.     }
  2030.  
  2031.     // Returns true if a bookmark is available
  2032.     HRESULT GetBookmark(CBookmark<>* pBookmark) const
  2033.     {
  2034.         HRESULT hr;
  2035.         if (m_pColumnInfo->iOrdinal == 0)
  2036.             hr = pBookmark->SetBookmark(m_pColumnInfo->ulColumnSize, (BYTE*)_GetDataPtr(0));
  2037.         else
  2038.             hr = E_FAIL;
  2039.         return hr;
  2040.     }
  2041.  
  2042.     ULONG GetColumnCount() const
  2043.     {
  2044.         return m_nColumns;
  2045.     }
  2046.  
  2047.     LPOLESTR GetColumnName(ULONG nColumn) const
  2048.     {
  2049.         if (TranslateColumnNo(nColumn))
  2050.             return m_pColumnInfo[nColumn].pwszName;
  2051.         else
  2052.             return NULL;
  2053.     }
  2054.  
  2055.     HRESULT GetColumnInfo(IRowset* pRowset, ULONG* pColumns, DBCOLUMNINFO** ppColumnInfo)
  2056.     {
  2057.         CComPtr<IColumnsInfo> spColumnsInfo;
  2058.         HRESULT hr = pRowset->QueryInterface(&spColumnsInfo);
  2059.         if (SUCCEEDED(hr))
  2060.             hr = spColumnsInfo->GetColumnInfo(pColumns, ppColumnInfo, &m_pStringsBuffer);
  2061.         
  2062.         return hr;
  2063.     }
  2064.  
  2065.     HRESULT AddBindEntry(const DBCOLUMNINFO& info)
  2066.     {
  2067.         m_pColumnInfo = (DBCOLUMNINFO*)CoTaskMemRealloc(m_pColumnInfo, (m_nColumns + 1) * sizeof(DBCOLUMNINFO));
  2068.         if (m_pColumnInfo == NULL)
  2069.             return E_OUTOFMEMORY;
  2070.  
  2071.         m_pColumnInfo[m_nColumns] = info;
  2072.         m_nColumns++;
  2073.  
  2074.         return S_OK;
  2075.     }
  2076.     // Free's any columns in the current record that need to be freed.
  2077.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  2078.     void FreeRecordMemory()
  2079.     {
  2080.         ULONG i;
  2081.  
  2082.         for (i = 0; i < m_nColumns; i++)
  2083.             CAccessorBase::FreeType(m_pColumnInfo[i].wType, (BYTE*)_GetDataPtr(i));
  2084.     }
  2085.  
  2086. // Implementation
  2087.     void* _GetDataPtr(ULONG nColumn) const
  2088.     {
  2089.         return m_pBuffer + (ULONG)m_pColumnInfo[nColumn].pTypeInfo;
  2090.     }
  2091.     bool GetInternalColumnNo(TCHAR* pColumnName, ULONG* pColumn) const
  2092.     {
  2093.         USES_CONVERSION;
  2094.         ULONG        i;
  2095.         ULONG        nSize = (lstrlen(pColumnName) + 1) * sizeof(OLECHAR);
  2096.         OLECHAR*    pOleColumnName = T2OLE(pColumnName);
  2097.  
  2098.         // Search through the columns trying to find a match
  2099.         for (i = 0; i < m_nColumns; i++)
  2100.         {
  2101.             if (memcmp(m_pColumnInfo[i].pwszName, pOleColumnName, nSize) == 0)
  2102.                 break;
  2103.         }
  2104.         if (i < m_nColumns)
  2105.         {
  2106.             *pColumn = i;
  2107.             return true;
  2108.         }
  2109.         else
  2110.             return false;   // Not Found
  2111.     }
  2112.     HRESULT BindColumns(IUnknown* pUnk)
  2113.     {
  2114.         ATLASSERT(pUnk != NULL);
  2115.         CComPtr<IAccessor> spAccessor;
  2116.         HRESULT hr = pUnk->QueryInterface(&spAccessor);
  2117.         if (FAILED(hr))
  2118.             return hr;
  2119.  
  2120.         ULONG   i;
  2121.         ULONG   nOffset = 0, nLengthOffset, nStatusOffset;
  2122.         
  2123.         // If the user hasn't specifed the column information to bind by calling AddBindEntry then
  2124.         // we get it ourselves
  2125.         if (m_pColumnInfo == NULL)
  2126.         {
  2127.             CComPtr<IColumnsInfo> spColumnsInfo;
  2128.             hr = pUnk->QueryInterface(&spColumnsInfo);
  2129.             if (FAILED(hr))
  2130.                 return hr;
  2131.  
  2132.             hr = spColumnsInfo->GetColumnInfo(&m_nColumns, &m_pColumnInfo, &m_pStringsBuffer);
  2133.             if (FAILED(hr))
  2134.                 return hr;
  2135.  
  2136.             m_bOverride = false;
  2137.         }
  2138.         else
  2139.             m_bOverride = true;
  2140.  
  2141.         DBBINDING* pBinding = NULL;
  2142.         ATLTRY(pBinding= new DBBINDING[m_nColumns]);
  2143.         if (pBinding == NULL)
  2144.             return E_OUTOFMEMORY;
  2145.  
  2146.         DBBINDING* pCurrent = pBinding;
  2147.         DBOBJECT*  pObject;
  2148.         for (i = 0; i < m_nColumns; i++)
  2149.         {
  2150.             // If it's a BLOB or the column size is large enough for us to treat it as
  2151.             // a BLOB then we also need to set up the DBOBJECT structure.
  2152.             if (m_pColumnInfo[i].ulColumnSize > 1024 || m_pColumnInfo[i].wType == DBTYPE_IUNKNOWN)
  2153.             {
  2154.                 pObject = NULL;
  2155.                 ATLTRY(pObject = new DBOBJECT);
  2156.                 if (pObject == NULL)
  2157.                     return E_OUTOFMEMORY;
  2158.                 pObject->dwFlags = STGM_READ;
  2159.                 pObject->iid     = IID_ISequentialStream;
  2160.                 m_pColumnInfo[i].wType        = DBTYPE_IUNKNOWN;
  2161.                 m_pColumnInfo[i].ulColumnSize    = sizeof(IUnknown*);
  2162.             }
  2163.             else
  2164.                 pObject = NULL;
  2165.  
  2166.             // If column is of type STR or WSTR increase length by 1
  2167.             // to accommodate the NULL terminator.
  2168.             if (m_pColumnInfo[i].wType == DBTYPE_STR ||
  2169.                 m_pColumnInfo[i].wType == DBTYPE_WSTR)
  2170.                     m_pColumnInfo[i].ulColumnSize += 1;
  2171.  
  2172.             nLengthOffset = AddOffset(nOffset, m_pColumnInfo[i].ulColumnSize);
  2173.             nStatusOffset = AddOffset(nLengthOffset, sizeof(ULONG));
  2174.             Bind(pCurrent, m_pColumnInfo[i].iOrdinal, m_pColumnInfo[i].wType,
  2175.                 m_pColumnInfo[i].ulColumnSize, m_pColumnInfo[i].bPrecision, m_pColumnInfo[i].bScale,
  2176.                 DBPARAMIO_NOTPARAM, nOffset,
  2177.                 nLengthOffset, nStatusOffset, pObject);
  2178.             pCurrent++;
  2179.  
  2180.             // Note that, as we're not using this for anything else, we're using the
  2181.             // pTypeInfo element to store the offset to our data.
  2182.             m_pColumnInfo[i].pTypeInfo = (ITypeInfo*)nOffset;
  2183.  
  2184.             nOffset = AddOffset(nStatusOffset, sizeof(DBSTATUS));
  2185.         }
  2186.         // Allocate the accessor memory if we haven't done so yet
  2187.         if (m_pAccessorInfo == NULL)
  2188.         {
  2189.             hr = AllocateAccessorMemory(1); // We only have one accessor
  2190.             if (FAILED(hr))
  2191.             {
  2192.                 delete [] pBinding;
  2193.                 return hr;
  2194.             }
  2195.             m_pAccessorInfo->bAutoAccessor = TRUE;
  2196.         }
  2197.  
  2198.         // Allocate enough memory for the data buffer and tell the rowset
  2199.         // Note that the rowset will free the memory in its destructor.
  2200.         m_pBuffer = NULL;
  2201.         ATLTRY(m_pBuffer = new BYTE[nOffset]);
  2202.         if (m_pBuffer == NULL)
  2203.         {
  2204.             delete [] pBinding;
  2205.             return E_OUTOFMEMORY;
  2206.         }
  2207.         hr = BindEntries(pBinding, m_nColumns, &m_pAccessorInfo->hAccessor,
  2208.                 nOffset, spAccessor);
  2209.         delete [] pBinding;
  2210.  
  2211.         return hr;
  2212.     }
  2213.  
  2214.     static ULONG AddOffset(ULONG nCurrent, ULONG nAdd)
  2215.     {
  2216.         struct foobar
  2217.         {
  2218.             char    foo;
  2219.             long    bar;
  2220.         };
  2221.         ULONG nAlign = offsetof(foobar, bar);
  2222.  
  2223.         return nCurrent + nAdd + (nAdd % nAlign);;
  2224.     }
  2225.  
  2226.     // Translate the column number to the index into the column info array
  2227.     bool TranslateColumnNo(ULONG& nColumn) const
  2228.     {
  2229.         ATLASSERT(m_pColumnInfo != NULL);
  2230.         // If the user has overriden the binding then we need to search
  2231.         // through the column info for the ordinal number
  2232.         if (m_bOverride)
  2233.         {
  2234.             for (ULONG i = 0; i < m_nColumns; i++)
  2235.             {
  2236.                 if (m_pColumnInfo[i].iOrdinal == nColumn)
  2237.                 {
  2238.                     nColumn = i;
  2239.                     return true;
  2240.                 }
  2241.             }
  2242.             return false;
  2243.         }
  2244.         else
  2245.         {
  2246.             // Note that m_pColumnInfo->iOrdinal will be zero if have bound
  2247.             // a bookmark as the first entry, otherwise it will be 1.
  2248.             // If the column is out of range then return false
  2249.             if (nColumn > (m_nColumns - 1 + m_pColumnInfo->iOrdinal))
  2250.                 return false;
  2251.  
  2252.             // otherwise translate the column to an index into our internal
  2253.             // binding entries array
  2254.             nColumn -= m_pColumnInfo->iOrdinal;
  2255.             return true;
  2256.         }
  2257.     }
  2258.     typedef CDynamicAccessor _OutputColumnsClass;
  2259.     static bool HasOutputColumns() { return true; }
  2260.  
  2261.     ULONG               m_nColumns;
  2262.     DBCOLUMNINFO*       m_pColumnInfo;
  2263.     OLECHAR*            m_pStringsBuffer;
  2264.     bool                m_bOverride;
  2265. };
  2266.  
  2267.  
  2268. ///////////////////////////////////////////////////////////////////////////
  2269. // class CDynamicParameterAccessor
  2270.  
  2271. class CDynamicParameterAccessor : public CDynamicAccessor
  2272. {
  2273. // Constructors and Destructors
  2274. public:
  2275.     typedef CDynamicParameterAccessor _ParamClass;
  2276.     CDynamicParameterAccessor()
  2277.     {
  2278.         m_pParameterEntry       = NULL;
  2279.         m_pParameterBuffer      = NULL;
  2280.         m_ppParamName           = NULL;
  2281.         m_nParameterBufferSize  = 0;
  2282.         m_nParams               = 0;
  2283.     };
  2284.  
  2285.     ~CDynamicParameterAccessor()
  2286.     {
  2287.         delete [] m_pParameterEntry;
  2288.         if (m_ppParamName != NULL)
  2289.         {
  2290.             if (*m_ppParamName != NULL)
  2291.                 CoTaskMemFree(*m_ppParamName);
  2292.             delete [] m_ppParamName;
  2293.         }
  2294.         delete m_pParameterBuffer;
  2295.     };
  2296.     // nParam is the parameter number (offset from 1)
  2297.     bool GetParamType(ULONG nParam, DBTYPE* pType) const
  2298.     {
  2299.         ATLASSERT(pType != NULL);
  2300.         if (nParam == 0 || nParam > m_nParams)
  2301.             return false;
  2302.  
  2303.         *pType = m_pParameterEntry[nParam-1].wType;
  2304.         return true;
  2305.     }
  2306.     template <class ctype>
  2307.     bool GetParam(ULONG nParam, ctype* pData) const
  2308.     {
  2309.         ATLASSERT(pData != NULL);
  2310.         ctype* pBuffer = (ctype*)GetParam(nParam);
  2311.         if (pBuffer == NULL)
  2312.             return false;
  2313.         *pData = *pBuffer;
  2314.         return true;
  2315.  
  2316.     }
  2317.     template <class ctype>
  2318.     bool SetParam(ULONG nParam, ctype* pData)
  2319.     {
  2320.         ATLASSERT(pData != NULL);
  2321.         ctype* pBuffer = (ctype*)GetParam(nParam);
  2322.         if (pBuffer == NULL)
  2323.             return false;
  2324.         *pBuffer = *pData;
  2325.         return true;
  2326.  
  2327.     }
  2328.     template <class ctype>
  2329.     bool GetParam(TCHAR* pParamName, ctype* pData) const
  2330.     {
  2331.         ATLASSERT(pData != NULL);
  2332.         ctype* pBuffer = (ctype*)GetParam(pParamName);
  2333.         if (pBuffer == NULL)
  2334.             return false;
  2335.         *pData = *pBuffer;
  2336.         return true;
  2337.  
  2338.     }
  2339.     template <class ctype>
  2340.     bool SetParam(TCHAR* pParamName, ctype* pData)
  2341.     {
  2342.         ATLASSERT(pData != NULL);
  2343.         ctype* pBuffer = (ctype*)GetParam(pParamName);
  2344.         if (pBuffer == NULL)
  2345.             return false;
  2346.         *pBuffer = *pData;
  2347.         return true;
  2348.  
  2349.     }
  2350.     void* GetParam(ULONG nParam) const
  2351.     {
  2352.         if (nParam == 0 || nParam > m_nParams)
  2353.             return NULL;
  2354.         else
  2355.             return m_pParameterBuffer + m_pParameterEntry[nParam-1].obValue;
  2356.     }
  2357.     void* GetParam(TCHAR* pParamName) const
  2358.     {
  2359.         USES_CONVERSION;
  2360.         ULONG    i;
  2361.         ULONG        nSize = (lstrlen(pParamName) + 1) * sizeof(OLECHAR);
  2362.         OLECHAR*    pOleParamName = T2OLE(pParamName);
  2363.  
  2364.         for (i=0; i<m_nParams; i++)
  2365.         {
  2366.             if (memcmp(m_ppParamName[i], pOleParamName, nSize) == 0)
  2367.                 break;
  2368.         }
  2369.         if (i < m_nParams)
  2370.             return (m_pParameterBuffer + m_pParameterEntry[i].obValue);
  2371.         else
  2372.             return NULL;    // Not Found
  2373.     }
  2374.     // Get the number of parameters
  2375.     ULONG GetParamCount() const
  2376.     {
  2377.         return m_nParams;
  2378.     }
  2379.     // Get the parameter name for the passed parameter number
  2380.     LPOLESTR GetParamName(ULONG ulParam) const
  2381.     {
  2382.         ATLASSERT((ulParam >= 0) && (ulParam<m_nParams));
  2383.         return m_ppParamName[ulParam];
  2384.     }
  2385.  
  2386. // Implementation
  2387.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand,
  2388.                 void** ppParameterBuffer)
  2389.     {
  2390.         // If we have already bound the parameters then just return
  2391.         // the pointer to the parameter buffer
  2392.         if (*pHAccessor != NULL)
  2393.         {
  2394.             *ppParameterBuffer = m_pParameterBuffer;
  2395.             return S_OK;
  2396.         }
  2397.  
  2398.         CComPtr<IAccessor> spAccessor;
  2399.         HRESULT hr = pCommand->QueryInterface(&spAccessor);
  2400.         if (FAILED(hr))
  2401.             return hr;
  2402.  
  2403.         // Try to bind parameters if available
  2404.         CComPtr<ICommandWithParameters> spCommandParameters;
  2405.         hr = pCommand->QueryInterface(&spCommandParameters);
  2406.         if (FAILED(hr))
  2407.             return hr;
  2408.  
  2409.         ULONG           ulParams     = 0;
  2410.         DBPARAMINFO*    pParamInfo   = NULL;
  2411.         LPOLESTR        pNamesBuffer = NULL;
  2412.  
  2413.         // Get Parameter Information
  2414.         hr = spCommandParameters->GetParameterInfo(&ulParams, &pParamInfo,
  2415.                 &pNamesBuffer);
  2416.         if (FAILED(hr))
  2417.             return hr;
  2418.  
  2419.         // Create the parameter information for binding
  2420.         hr = AllocateParameterInfo(ulParams);
  2421.         if (FAILED(hr))
  2422.         {
  2423.             CoTaskMemFree(pParamInfo);
  2424.             CoTaskMemFree(pNamesBuffer);
  2425.             return hr;
  2426.         }
  2427.  
  2428.         ULONG nOffset = 0;
  2429.         DBBINDING* pCurrent = m_pParameterEntry;
  2430.         for (ULONG l=0; l<ulParams; l++)
  2431.         {
  2432.             m_pParameterEntry[l].eParamIO = 0;
  2433.             if (pParamInfo[l].dwFlags & DBPARAMFLAGS_ISINPUT)
  2434.                 m_pParameterEntry[l].eParamIO |= DBPARAMIO_INPUT;
  2435.  
  2436.             if (pParamInfo[l].dwFlags & DBPARAMFLAGS_ISOUTPUT)
  2437.                 m_pParameterEntry[l].eParamIO |= DBPARAMIO_OUTPUT;
  2438.  
  2439.             Bind(pCurrent, pParamInfo[l].iOrdinal, pParamInfo[l].wType,
  2440.                 pParamInfo[l].ulParamSize, pParamInfo[l].bPrecision, pParamInfo[l].bScale,
  2441.                 m_pParameterEntry[l].eParamIO, nOffset);
  2442.             pCurrent++;
  2443.  
  2444.             m_ppParamName[l] = pNamesBuffer;
  2445.             if (pNamesBuffer && *pNamesBuffer)
  2446.             {
  2447.                 // Search for the NULL termination character
  2448.                 while (*pNamesBuffer++)
  2449.                     ;
  2450.             }
  2451.             nOffset += pParamInfo[l].ulParamSize;
  2452.  
  2453.         }
  2454.  
  2455.         // Allocate memory for the new buffer
  2456.         m_pParameterBuffer = NULL;
  2457.         ATLTRY(m_pParameterBuffer = new BYTE[nOffset]);
  2458.         if (m_pParameterBuffer == NULL)
  2459.         {
  2460.             // Note that pNamesBuffer will be freed in the destructor
  2461.             // by freeing *m_ppParamName
  2462.             CoTaskMemFree(pParamInfo);
  2463.             return E_OUTOFMEMORY;
  2464.         }
  2465.         *ppParameterBuffer = m_pParameterBuffer;
  2466.         m_nParameterBufferSize = nOffset;
  2467.         m_nParams = ulParams;
  2468.         BindEntries(m_pParameterEntry, ulParams, pHAccessor, nOffset, spAccessor);
  2469.  
  2470.         CoTaskMemFree(pParamInfo);
  2471.  
  2472.         return S_OK;
  2473.     }
  2474.     bool HasParameters() const
  2475.     {
  2476.         return true;
  2477.     }
  2478.     HRESULT AllocateParameterInfo(int nParamEntries)
  2479.     {
  2480.         // Allocate memory for the bind structures
  2481.         m_pParameterEntry = NULL;
  2482.         ATLTRY(m_pParameterEntry = new DBBINDING[nParamEntries]);
  2483.         if (m_pParameterEntry == NULL)
  2484.             return E_OUTOFMEMORY;
  2485.  
  2486.         // Allocate memory to store the field names
  2487.         m_ppParamName = NULL;
  2488.         ATLTRY(m_ppParamName = new OLECHAR*[nParamEntries]);
  2489.         if (m_ppParamName == NULL)
  2490.             return E_OUTOFMEMORY;
  2491.         return S_OK;
  2492.     }
  2493.  
  2494. // Data Members
  2495.     // Number of parameters
  2496.     ULONG               m_nParams;
  2497.     // A pointer to the entry structures for each parameter
  2498.     DBBINDING*            m_pParameterEntry;
  2499.     // String names for the parameters
  2500.     OLECHAR**           m_ppParamName;
  2501.     // The size of the buffer where the parameters are stored
  2502.     ULONG               m_nParameterBufferSize;
  2503.     // A pointer to the buffer where the parameters are stored
  2504.     BYTE*               m_pParameterBuffer;
  2505. };
  2506.  
  2507.  
  2508. ///////////////////////////////////////////////////////////////////////////
  2509. // class CManualAccessor
  2510.  
  2511. class CManualAccessor :
  2512.     public CAccessorBase
  2513. {
  2514. public:
  2515.     CManualAccessor()
  2516.     {
  2517.         // By default we don't have any parameters unless CreateParameterAccessor is called
  2518.         m_pEntry          = NULL;
  2519.         m_nParameters     = 0;
  2520.         m_pParameterEntry = NULL;
  2521.         m_nColumns        = 0;
  2522.     }
  2523.     ~CManualAccessor()
  2524.     {
  2525.         delete [] m_pEntry;
  2526.         delete [] m_pParameterEntry;
  2527.     }
  2528.     HRESULT CreateAccessor(int nBindEntries, void* pBuffer, ULONG nBufferSize)
  2529.     {
  2530.         m_pBuffer     = (BYTE*)pBuffer;
  2531.         m_nBufferSize = nBufferSize;
  2532.         m_nColumns    = nBindEntries;
  2533.         m_nEntry      = 0;
  2534.  
  2535.         // If they've previously created some entries then free them
  2536.         delete [] m_pEntry;
  2537.         m_pEntry = NULL;
  2538.  
  2539.         // Allocate memory for the bind structures
  2540.         ATLTRY(m_pEntry = new DBBINDING[nBindEntries]);
  2541.         if (m_pEntry == NULL)
  2542.             return E_OUTOFMEMORY;
  2543.         else
  2544.             return S_OK;
  2545.     }
  2546.     HRESULT CreateParameterAccessor(int nBindEntries, void* pBuffer, ULONG nBufferSize)
  2547.     {
  2548.         m_pParameterBuffer     = (BYTE*)pBuffer;
  2549.         m_nParameterBufferSize = nBufferSize;
  2550.         m_nParameters          = nBindEntries;
  2551.         m_nCurrentParameter    = 0;
  2552.  
  2553.         // Allocate memory for the bind structures
  2554.         m_pParameterEntry = NULL;
  2555.         ATLTRY(m_pParameterEntry  = new DBBINDING[nBindEntries]);
  2556.         if (m_pParameterEntry == NULL)
  2557.             return E_OUTOFMEMORY;
  2558.         else
  2559.             return S_OK;
  2560.     }
  2561.     void AddBindEntry(ULONG nOrdinal, DBTYPE wType, ULONG nColumnSize,
  2562.             void* pData, void* pLength = NULL, void* pStatus = NULL)
  2563.     {
  2564.         ATLASSERT(m_nEntry < m_nColumns);
  2565.         ULONG   nLengthOffset, nStatusOffset;
  2566.  
  2567.         if (pStatus != NULL)
  2568.             nStatusOffset = (BYTE*)pStatus - m_pBuffer;
  2569.         else
  2570.             nStatusOffset = 0;
  2571.  
  2572.         if (pLength != NULL)
  2573.             nLengthOffset = (BYTE*)pLength - m_pBuffer;
  2574.         else
  2575.             nLengthOffset = 0;
  2576.  
  2577.         Bind(m_pEntry+m_nEntry, nOrdinal, wType, nColumnSize, 0, 0, DBPARAMIO_NOTPARAM,
  2578.             (BYTE*)pData - m_pBuffer, nLengthOffset, nStatusOffset);
  2579.  
  2580.         m_nEntry++;
  2581.     }
  2582.     void AddParameterEntry(ULONG nOrdinal, DBTYPE wType, ULONG nColumnSize,
  2583.             void* pData, void* pLength = NULL, void* pStatus = NULL,
  2584.             DBPARAMIO eParamIO = DBPARAMIO_INPUT)
  2585.     {
  2586.         ATLASSERT(m_nCurrentParameter < m_nParameters);
  2587.         ULONG   nLengthOffset, nStatusOffset;
  2588.  
  2589.         if (pStatus != NULL)
  2590.             nStatusOffset = (BYTE*)pStatus - m_pParameterBuffer;
  2591.         else
  2592.             nStatusOffset = 0;
  2593.  
  2594.         if (pLength != NULL)
  2595.             nLengthOffset = (BYTE*)pLength - m_pBuffer;
  2596.         else
  2597.             nLengthOffset = 0;
  2598.  
  2599.         Bind(m_pParameterEntry + m_nCurrentParameter, nOrdinal, wType, nColumnSize, 0, 0,
  2600.             eParamIO, (BYTE*)pData - m_pParameterBuffer, nLengthOffset, nStatusOffset);
  2601.  
  2602.         m_nCurrentParameter++;
  2603.     }
  2604.     // Free's any columns in the current record that need to be freed.
  2605.     // E.g. Calls SysFreeString on any BSTR's and Release on any interfaces.
  2606.     void FreeRecordMemory()
  2607.     {
  2608.         ULONG i;
  2609.  
  2610.         for (i = 0; i < m_nColumns; i++)
  2611.             CAccessorBase::FreeType(m_pEntry[i].wType, m_pBuffer + m_pEntry[i].obValue);
  2612.     }
  2613.  
  2614. // Implementation
  2615.     HRESULT BindColumns(IUnknown* pUnk)
  2616.     {
  2617.         ATLASSERT(pUnk != NULL);
  2618.         CComPtr<IAccessor> spAccessor;
  2619.         HRESULT hr = pUnk->QueryInterface(&spAccessor);
  2620.         if (FAILED(hr))
  2621.             return hr;
  2622.  
  2623.         // Allocate the accessor memory if we haven't done so yet
  2624.         if (m_pAccessorInfo == NULL)
  2625.         {
  2626.             hr = AllocateAccessorMemory(1); // We only have one accessor
  2627.             if (FAILED(hr))
  2628.                 return hr;
  2629.             m_pAccessorInfo->bAutoAccessor = TRUE;
  2630.         }
  2631.  
  2632.         return BindEntries(m_pEntry, m_nColumns, &m_pAccessorInfo->hAccessor, m_nBufferSize, spAccessor);
  2633.     }
  2634.  
  2635.     HRESULT BindParameters(HACCESSOR* pHAccessor, ICommand* pCommand, void** ppParameterBuffer)
  2636.     {
  2637.         HRESULT hr;
  2638.         *ppParameterBuffer = m_pParameterBuffer;
  2639.         
  2640.         // Only bind the parameter if we haven't done so yet
  2641.         if (*pHAccessor == NULL)
  2642.         {
  2643.             // Get the IAccessor from the passed IUnknown
  2644.             CComPtr<IAccessor> spAccessor;
  2645.             hr = pCommand->QueryInterface(&spAccessor);
  2646.             if (SUCCEEDED(hr))
  2647.             {
  2648.                 hr = BindEntries(m_pParameterEntry, m_nParameters, pHAccessor,
  2649.                         m_nParameterBufferSize, spAccessor);
  2650.             }
  2651.         }
  2652.         else
  2653.             hr = S_OK;
  2654.         
  2655.         return hr;
  2656.     }
  2657.     typedef CManualAccessor _ParamClass;
  2658.     bool HasParameters() { return (m_nParameters > 0); }
  2659.     typedef CManualAccessor _OutputColumnsClass;
  2660.     static bool HasOutputColumns() { return true; }
  2661.     ULONG GetColumnCount() const
  2662.     {
  2663.         return m_nColumns;
  2664.     }
  2665.  
  2666.     // The binding structure for the output columns
  2667.     DBBINDING*          m_pEntry;
  2668.     // The number of output columns
  2669.     ULONG               m_nColumns;
  2670.     // The number of the current entry for the output columns
  2671.     ULONG               m_nEntry;
  2672.     // The size of the data buffer for the output columns
  2673.     ULONG               m_nBufferSize;
  2674.     // The number of parameters columns
  2675.     ULONG               m_nParameters;
  2676.     // The number of the parameter column to bind next
  2677.     ULONG               m_nCurrentParameter;
  2678.     // A pointer to the entry structures for each parameter
  2679.     DBBINDING*          m_pParameterEntry;
  2680.     // The size of the buffer where the parameters are stored
  2681.     ULONG               m_nParameterBufferSize;
  2682.     // A pointer to the buffer where the parameters are stored
  2683.     BYTE*               m_pParameterBuffer;
  2684. };
  2685.  
  2686.  
  2687. ///////////////////////////////////////////////////////////////////////////
  2688. // CAccessorRowset
  2689.  
  2690. template <class TAccessor = CNoAccessor, class TRowset = CRowset>
  2691. class CAccessorRowset :
  2692.     public TAccessor,
  2693.     public TRowset
  2694. {
  2695. public:
  2696.     CAccessorRowset()
  2697.     {
  2698.         // Give the rowset a pointer to the accessor
  2699.         SetAccessor(this);
  2700.     }
  2701.     ~CAccessorRowset()
  2702.     {
  2703.         Close();
  2704.     }
  2705.     // Used to get the column information from the opened rowset. The user is responsible
  2706.     // for freeing the returned column information and string buffer.
  2707.     HRESULT GetColumnInfo(ULONG* pulColumns,
  2708.         DBCOLUMNINFO** ppColumnInfo, LPOLESTR* ppStrings) const
  2709.     {
  2710.         ATLASSERT(GetIRowset() != NULL);
  2711.         if (ppColumnInfo == NULL || pulColumns == NULL || ppStrings == NULL)
  2712.             return E_POINTER;
  2713.  
  2714.         CComPtr<IColumnsInfo> spColumns;
  2715.         HRESULT hr = GetIRowset()->QueryInterface(&spColumns);
  2716.         if (SUCCEEDED(hr))
  2717.             hr = spColumns->GetColumnInfo(pulColumns, ppColumnInfo, ppStrings);
  2718.  
  2719.         return hr;
  2720.     }
  2721.     // Used to get the column information when overriding the bindings using CDynamicAccessor
  2722.     // The user should CoTaskMemFree the column information pointer that is returned.
  2723.     HRESULT GetColumnInfo(ULONG* pColumns, DBCOLUMNINFO** ppColumnInfo)
  2724.     {
  2725.         // If you get a compilation here, then you are most likely calling this function
  2726.         // from a class that is not using CDynamicAccessor.
  2727.         ATLASSERT(GetIRowset() != NULL);
  2728.         return TAccessor::GetColumnInfo(GetIRowset(), pColumns, ppColumnInfo);
  2729.     }
  2730.     // Call to bind the output columns
  2731.     HRESULT Bind()
  2732.     {
  2733.         // Bind should only be called when we've successfully opened the rowset
  2734.         ATLASSERT(GetIRowset() != NULL);
  2735.         HRESULT hr = TAccessor::BindColumns(GetIRowset());
  2736.         if (SUCCEEDED(hr))
  2737.             hr = BindFinished();
  2738.         return hr;
  2739.     }
  2740.     // Close the opened rowset and release the created accessors for the output columns
  2741.     void Close()
  2742.     {
  2743.         if (GetIRowset() != NULL)
  2744.         {
  2745.             ReleaseAccessors(GetIRowset());
  2746.             TAccessor::Close();
  2747.             TRowset::Close();
  2748.         }
  2749.     }
  2750. };
  2751.  
  2752.  
  2753. ///////////////////////////////////////////////////////////////////////////
  2754. // class CEnumeratorAccessor
  2755.  
  2756. class CEnumeratorAccessor
  2757. {
  2758. public:
  2759.     WCHAR           m_szName[129];
  2760.     WCHAR           m_szParseName[129];
  2761.     WCHAR           m_szDescription[129];
  2762.     USHORT          m_nType;
  2763.     VARIANT_BOOL    m_bIsParent;
  2764.  
  2765. // Binding Maps
  2766. BEGIN_COLUMN_MAP(CEnumeratorAccessor)
  2767.     COLUMN_ENTRY(1, m_szName)
  2768.     COLUMN_ENTRY(2, m_szParseName)
  2769.     COLUMN_ENTRY(3, m_szDescription)
  2770.     COLUMN_ENTRY(4, m_nType)
  2771.     COLUMN_ENTRY(5, m_bIsParent)
  2772. END_COLUMN_MAP()
  2773. };
  2774.  
  2775.  
  2776. ///////////////////////////////////////////////////////////////////////////
  2777. // class CEnumerator
  2778.  
  2779. class CEnumerator : public CAccessorRowset<CAccessor<CEnumeratorAccessor> >
  2780. {
  2781. public:
  2782.     HRESULT Open(LPMONIKER pMoniker)
  2783.     {
  2784.         if (pMoniker == NULL)
  2785.             return E_FAIL;
  2786.  
  2787.         // Bind the moniker for the sources rowset
  2788.         if (FAILED(BindMoniker(pMoniker, 0, IID_ISourcesRowset,
  2789.                     (void**)&m_spSourcesRowset)))
  2790.             return E_FAIL;
  2791.  
  2792.         // Enumerate the data sources
  2793.         if (FAILED(m_spSourcesRowset->GetSourcesRowset(NULL, IID_IRowset, 0,
  2794.             NULL, (IUnknown**)&m_spRowset)))
  2795.             return E_FAIL;
  2796.  
  2797.         return Bind();
  2798.     }
  2799.     HRESULT Open(const CEnumerator& enumerator)
  2800.     {
  2801.         HRESULT hr;
  2802.         CComPtr<IMoniker> spMoniker;
  2803.  
  2804.         hr = enumerator.GetMoniker(&spMoniker);
  2805.         if (FAILED(hr))
  2806.             return hr;
  2807.  
  2808.         return Open(spMoniker);
  2809.     }
  2810.     HRESULT Open(const CLSID* pClsid = &CLSID_OLEDB_ENUMERATOR)
  2811.     {
  2812.         if (pClsid == NULL)
  2813.             return E_FAIL;
  2814.  
  2815.         HRESULT hr;
  2816.         // Create the enumerator
  2817.         hr = CoCreateInstance(*pClsid, NULL, CLSCTX_INPROC_SERVER,
  2818.                 IID_ISourcesRowset, (LPVOID*)&m_spSourcesRowset);
  2819.         if (FAILED(hr))
  2820.             return hr;
  2821.  
  2822.         // Get the rowset so we can enumerate the data sources
  2823.         hr = m_spSourcesRowset->GetSourcesRowset(NULL, IID_IRowset, 0,
  2824.             NULL, (IUnknown**)&m_spRowset);
  2825.         if (FAILED(hr))
  2826.             return hr;
  2827.  
  2828.         return Bind();
  2829.     }
  2830.  
  2831.     HRESULT GetMoniker(LPMONIKER* ppMoniker) const
  2832.     {
  2833.         CComPtr<IParseDisplayName> spParse;
  2834.         HRESULT hr;
  2835.         ULONG   chEaten;
  2836.  
  2837.         if (ppMoniker == NULL)
  2838.             return E_POINTER;
  2839.  
  2840.         if (m_spSourcesRowset == NULL)
  2841.             return E_FAIL;
  2842.  
  2843.         hr = m_spSourcesRowset->QueryInterface(IID_IParseDisplayName, (void**)&spParse);
  2844.         if (FAILED(hr))
  2845.             return hr;
  2846.  
  2847.         hr = spParse->ParseDisplayName(NULL, (LPOLESTR)m_szParseName,
  2848.                 &chEaten, ppMoniker);
  2849.         return hr;
  2850.     }
  2851.  
  2852.     HRESULT GetMoniker(LPMONIKER* ppMoniker, LPCTSTR lpszDisplayName) const
  2853.     {
  2854.         USES_CONVERSION;
  2855.         CComPtr<IParseDisplayName> spParse;
  2856.         HRESULT hr;
  2857.         ULONG   chEaten;
  2858.  
  2859.         if (ppMoniker == NULL || lpszDisplayName == NULL)
  2860.             return E_POINTER;
  2861.  
  2862.         if (m_spSourcesRowset == NULL)
  2863.             return E_FAIL;
  2864.  
  2865.         hr = m_spSourcesRowset->QueryInterface(IID_IParseDisplayName, (void**)&spParse);
  2866.         if (FAILED(hr))
  2867.             return hr;
  2868.  
  2869.         hr = spParse->ParseDisplayName(NULL, (LPOLESTR)T2COLE(lpszDisplayName),
  2870.                 &chEaten, ppMoniker);
  2871.         return hr;
  2872.     }
  2873.  
  2874.     bool Find(TCHAR* szSearchName)
  2875.     {
  2876.         USES_CONVERSION;
  2877.         // Loop through the providers looking for the passed name
  2878.         while (MoveNext()==S_OK && lstrcmp(W2T(m_szName), szSearchName))
  2879.             ATLTRACE2(atlTraceDBClient, 0, _T("%s, %s, %d\n"), W2T(m_szName), W2T(m_szParseName), m_nType);
  2880.         if (lstrcmp(W2T(m_szName), szSearchName))
  2881.             return false;
  2882.         else
  2883.             return true;
  2884.     }
  2885.  
  2886.     CComPtr<ISourcesRowset> m_spSourcesRowset;
  2887. };
  2888.  
  2889.  
  2890. ///////////////////////////////////////////////////////////////////////////
  2891. // CDataSource
  2892.  
  2893. class CDataSource
  2894. {
  2895. public:
  2896.     HRESULT Open(const CLSID& clsid, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  2897.     {
  2898.         HRESULT hr;
  2899.  
  2900.         m_spInit.Release();
  2901.         hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,
  2902.                 (void**)&m_spInit);
  2903.         if (FAILED(hr))
  2904.             return hr;
  2905.  
  2906.         // Initialize the provider
  2907.         return OpenWithProperties(pPropSet, nPropertySets);
  2908.     }
  2909.     HRESULT Open(const CLSID& clsid, LPCTSTR pName, LPCTSTR pUserName = NULL,
  2910.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  2911.     {
  2912.         HRESULT   hr;
  2913.  
  2914.         m_spInit.Release();
  2915.         hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,
  2916.                 (void**)&m_spInit);
  2917.         if (FAILED(hr))
  2918.             return hr;
  2919.  
  2920.         return OpenWithNameUserPassword(pName, pUserName, pPassword, nInitMode);
  2921.     }
  2922.     HRESULT Open(LPCTSTR szProgID, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  2923.     {
  2924.         USES_CONVERSION;
  2925.         HRESULT hr;
  2926.         CLSID   clsid;
  2927.  
  2928.         hr = CLSIDFromProgID(T2COLE(szProgID), &clsid);
  2929.         if (FAILED(hr))
  2930.             return hr;
  2931.  
  2932.         return Open(clsid, pPropSet, nPropertySets);
  2933.     }
  2934.     HRESULT Open(LPCTSTR szProgID, LPCTSTR pName, LPCTSTR pUserName = NULL,
  2935.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  2936.     {
  2937.         USES_CONVERSION;
  2938.         HRESULT hr;
  2939.         CLSID   clsid;
  2940.  
  2941.         hr = CLSIDFromProgID(T2COLE(szProgID), &clsid);
  2942.         if (FAILED(hr))
  2943.             return hr;
  2944.  
  2945.         return Open(clsid, pName, pUserName, pPassword, nInitMode);
  2946.     }
  2947.     HRESULT Open(const CEnumerator& enumerator, DBPROPSET* pPropSet = NULL, ULONG nPropertySets=1)
  2948.     {
  2949.         CComPtr<IMoniker> spMoniker;
  2950.         HRESULT   hr;
  2951.  
  2952.         hr = enumerator.GetMoniker(&spMoniker);
  2953.         if (FAILED(hr))
  2954.             return hr;
  2955.  
  2956.         m_spInit.Release();
  2957.         //  Now bind the moniker
  2958.         hr = BindMoniker(spMoniker, 0, IID_IDBInitialize, (void**)&m_spInit);
  2959.         if (FAILED(hr))
  2960.             return hr;
  2961.  
  2962.         return OpenWithProperties(pPropSet, nPropertySets);
  2963.     }
  2964.     HRESULT Open(const CEnumerator& enumerator, LPCTSTR pName, LPCTSTR pUserName = NULL,
  2965.         LPCTSTR pPassword = NULL, long nInitMode = 0)
  2966.     {
  2967.         CComPtr<IMoniker> spMoniker;
  2968.         HRESULT   hr;
  2969.  
  2970.         hr = enumerator.GetMoniker(&spMoniker);
  2971.         if (FAILED(hr))
  2972.             return hr;
  2973.  
  2974.         m_spInit.Release();
  2975.         //  Now bind the moniker
  2976.         hr = BindMoniker(spMoniker, 0, IID_IDBInitialize, (void**)&m_spInit);
  2977.         if (FAILED(hr))
  2978.             return hr;
  2979.  
  2980.         return OpenWithNameUserPassword(pName, pUserName, pPassword, nInitMode);
  2981.     }
  2982.     // Invoke the data links dialog and open the selected database
  2983.     HRESULT Open(HWND hWnd = GetActiveWindow(), DBPROMPTOPTIONS dwPromptOptions = DBPROMPTOPTIONS_WIZARDSHEET)
  2984.     {
  2985.         CComPtr<IDBPromptInitialize> spDBInit;
  2986.  
  2987.         HRESULT hr = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
  2988.             IID_IDBPromptInitialize, (void**) &spDBInit);
  2989.         if (FAILED(hr))
  2990.             return hr;
  2991.  
  2992.         CComPtr<IDBProperties> spIDBProperties;
  2993.         hr = spDBInit->PromptDataSource(hWnd, dwPromptOptions, 0, NULL, NULL,
  2994.             IID_IDBProperties, (IUnknown**)&spIDBProperties);
  2995.  
  2996.         if (hr == S_OK)
  2997.             hr = OpenFromIDBProperties(spIDBProperties);
  2998.         else if (hr == S_FALSE)
  2999.             hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_CANCELLED);  // The user clicked cancel
  3000.  
  3001.         return hr;
  3002.     }
  3003.     // Bring up the "Organize Dialog" which allows the user to select a previously created data link
  3004.     // file (.MDL file). The selected file will be used to open the datbase.
  3005.     HRESULT OpenWithPromptFileName(HWND hWnd = GetActiveWindow(), DBPROMPTOPTIONS dwPromptOptions = DBPROMPTOPTIONS_NONE,
  3006.         LPCOLESTR szInitialDirectory = NULL)
  3007.     {
  3008.         USES_CONVERSION;
  3009.         CComPtr<IDBPromptInitialize> spDBInit;
  3010.  
  3011.         HRESULT hr = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
  3012.             IID_IDBPromptInitialize, (void**) &spDBInit);
  3013.         if (FAILED(hr))
  3014.             return hr;
  3015.  
  3016.         CComPtr<IDBProperties> spIDBProperties;
  3017.         LPOLESTR szSelected;
  3018.  
  3019.         hr = spDBInit->PromptFileName(hWnd, dwPromptOptions, szInitialDirectory, L"*.mdl", &szSelected);
  3020.  
  3021.         if (hr == S_OK)
  3022.             hr = OpenFromFileName(szSelected);
  3023.         else if (hr == S_FALSE)
  3024.             hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_CANCELLED);  // The user clicked cancel
  3025.  
  3026.         return hr;
  3027.     }
  3028.     // Open the datasource specified by the passed filename, typically a .MDL file
  3029.     HRESULT OpenFromFileName(LPCOLESTR szFileName)
  3030.     {
  3031.         CComPtr<IDataInitialize> spDataInit;
  3032.         LPOLESTR                 szInitString;
  3033.  
  3034.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3035.             IID_IDataInitialize, (void**)&spDataInit);
  3036.         if (FAILED(hr))
  3037.             return hr;
  3038.  
  3039.         hr = spDataInit->LoadStringFromStorage(szFileName, &szInitString);
  3040.         if (FAILED(hr))
  3041.             return hr;
  3042.  
  3043.         return OpenFromInitializationString(szInitString);
  3044.     }
  3045.     // Open the datasource specified by the passed initialization string
  3046.     HRESULT OpenFromInitializationString(LPCOLESTR szInitializationString)
  3047.     {
  3048.         CComPtr<IDataInitialize> spDataInit;
  3049.  
  3050.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3051.             IID_IDataInitialize, (void**)&spDataInit);
  3052.         if (FAILED(hr))
  3053.             return hr;
  3054.  
  3055.         CComPtr<IDBProperties> spIDBProperties;
  3056.         hr = spDataInit->GetDataSource(NULL, CLSCTX_INPROC_SERVER, szInitializationString,
  3057.             IID_IDBProperties, (IUnknown**)&spIDBProperties);
  3058.         if (FAILED(hr))
  3059.             return hr;
  3060.  
  3061.         return OpenFromIDBProperties(spIDBProperties);
  3062.     }
  3063.     // Get the initialization string from the currently open data source. The returned string
  3064.     // must be CoTaskMemFree'd when finished with.
  3065.     HRESULT GetInitializationString(BSTR* pInitializationString, bool bIncludePassword=false)
  3066.     {
  3067.         // If the datasource isn't open then we're not going to get an init string
  3068.         _ASSERTE(m_spInit != NULL);
  3069.         CComPtr<IDataInitialize> spDataInit;
  3070.         LPOLESTR    szInitString;
  3071.  
  3072.         HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
  3073.             IID_IDataInitialize, (void**)&spDataInit);
  3074.         if (FAILED(hr))
  3075.             return hr;
  3076.  
  3077.         hr = spDataInit->GetInitializationString(m_spInit, bIncludePassword, &szInitString);
  3078.  
  3079.         if (SUCCEEDED(hr))
  3080.             *pInitializationString = ::SysAllocString(szInitString);
  3081.  
  3082.         return hr;
  3083.     }
  3084.     HRESULT GetProperties(ULONG ulPropIDSets, const DBPROPIDSET* pPropIDSet,
  3085.                 ULONG* pulPropertySets, DBPROPSET** ppPropsets) const
  3086.     {
  3087.         CComPtr<IDBProperties> spProperties;
  3088.  
  3089.         // Check that we are connected
  3090.         ATLASSERT(m_spInit != NULL);
  3091.  
  3092.         HRESULT hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3093.         if (FAILED(hr))
  3094.             return hr;
  3095.  
  3096.         hr = spProperties->GetProperties(ulPropIDSets, pPropIDSet, pulPropertySets,
  3097.                 ppPropsets);
  3098.         return hr;
  3099.     }
  3100.  
  3101.     HRESULT GetProperty(const GUID& guid, DBPROPID propid, VARIANT* pVariant) const
  3102.     {
  3103.         ATLASSERT(pVariant != NULL);
  3104.         CComPtr<IDBProperties> spProperties;
  3105.  
  3106.         // Check that we are connected
  3107.         ATLASSERT(m_spInit != NULL);
  3108.  
  3109.         HRESULT hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3110.         if (FAILED(hr))
  3111.             return hr;
  3112.  
  3113.         CDBPropIDSet set(guid);
  3114.         set.AddPropertyID(propid);
  3115.         DBPROPSET* pPropSet = NULL;
  3116.         ULONG ulPropSet = 0;
  3117.         hr = spProperties->GetProperties(1, &set, &ulPropSet, &pPropSet);
  3118.         if (FAILED(hr))
  3119.             return hr;
  3120.  
  3121.         ATLASSERT(ulPropSet == 1);
  3122.         VariantCopy(pVariant, &pPropSet->rgProperties[0].vValue);
  3123.         CoTaskMemFree(pPropSet->rgProperties);
  3124.         CoTaskMemFree(pPropSet);
  3125.  
  3126.         return S_OK;
  3127.     }
  3128.     void Close()
  3129.     {
  3130.         m_spInit.Release();
  3131.     }
  3132.  
  3133. // Implementation
  3134.     HRESULT OpenFromIDBProperties(IDBProperties* pIDBProperties)
  3135.     {
  3136.         CComPtr<IPersist> spPersist;
  3137.         CLSID    clsid;
  3138.         HRESULT    hr;
  3139.  
  3140.         hr = pIDBProperties->QueryInterface(IID_IPersist, (void**)&spPersist);
  3141.         if (FAILED(hr))
  3142.             return hr;
  3143.  
  3144.         spPersist->GetClassID(&clsid);
  3145.  
  3146.         ULONG         ulPropSets=0;
  3147.         CDBPropSet* pPropSets=NULL;
  3148.         pIDBProperties->GetProperties(0, NULL, &ulPropSets, (DBPROPSET**)&pPropSets);
  3149.  
  3150.         hr = Open(clsid, &pPropSets[0], ulPropSets);
  3151.  
  3152.         for (ULONG i=0; i < ulPropSets; i++)
  3153.             (pPropSets+i)->~CDBPropSet();
  3154.         CoTaskMemFree(pPropSets);
  3155.  
  3156.         return hr;
  3157.     }
  3158.     HRESULT OpenWithNameUserPassword(LPCTSTR pName, LPCTSTR pUserName, LPCTSTR pPassword, long nInitMode = 0)
  3159.     {
  3160.         ATLASSERT(m_spInit != NULL);
  3161.         CComPtr<IDBProperties>  spProperties;
  3162.         HRESULT                 hr;
  3163.  
  3164.         hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3165.         if (FAILED(hr))
  3166.             return hr;
  3167.  
  3168.         // Set connection properties
  3169.         CDBPropSet propSet(DBPROPSET_DBINIT);
  3170.  
  3171.         // Add Datbase name, User name and Password
  3172.         if (pName != NULL)
  3173.             propSet.AddProperty(DBPROP_INIT_DATASOURCE, pName);
  3174.  
  3175.         if (pUserName != NULL)
  3176.             propSet.AddProperty(DBPROP_AUTH_USERID, pUserName);
  3177.  
  3178.         if (pPassword != NULL)
  3179.             propSet.AddProperty(DBPROP_AUTH_PASSWORD, pPassword);
  3180.  
  3181.         if (nInitMode)
  3182.             propSet.AddProperty(DBPROP_INIT_MODE, nInitMode);
  3183.  
  3184.         hr = spProperties->SetProperties(1, &propSet);
  3185.         if (FAILED(hr))
  3186.             return hr;
  3187.  
  3188.         // Initialize the provider
  3189.         return m_spInit->Initialize();
  3190.     }
  3191.     HRESULT OpenWithProperties(DBPROPSET* pPropSet, ULONG nPropertySets=1)
  3192.     {
  3193.         ATLASSERT(m_spInit != NULL);
  3194.  
  3195.         // Set the properties if there are some to set
  3196.         if (pPropSet != NULL)
  3197.         {
  3198.             CComPtr<IDBProperties>  spProperties;
  3199.             HRESULT                 hr;
  3200.  
  3201.             hr = m_spInit->QueryInterface(IID_IDBProperties, (void**)&spProperties);
  3202.             if (FAILED(hr))
  3203.                 return hr;
  3204.  
  3205.             hr = spProperties->SetProperties(nPropertySets, pPropSet);
  3206.             if (FAILED(hr))
  3207.                 return hr;
  3208.         }
  3209.  
  3210.         // Initialize the provider
  3211.         return m_spInit->Initialize();
  3212.     }
  3213.  
  3214.     CComPtr<IDBInitialize>  m_spInit;
  3215. };
  3216.  
  3217.  
  3218. ///////////////////////////////////////////////////////////////////////////
  3219. // class CSession
  3220.  
  3221. class CSession
  3222. {
  3223. public:
  3224.     // Create a session on the passed datasource
  3225.     HRESULT Open(const CDataSource& ds)
  3226.     {
  3227.         CComPtr<IDBCreateSession> spSession;
  3228.  
  3229.         // Check we have connected to the database
  3230.         ATLASSERT(ds.m_spInit != NULL);
  3231.  
  3232.         HRESULT hr = ds.m_spInit->QueryInterface(IID_IDBCreateSession, (void**)&spSession);
  3233.         if (FAILED(hr))
  3234.             return hr;
  3235.  
  3236.         hr = spSession->CreateSession(NULL, IID_IOpenRowset, (IUnknown**)&m_spOpenRowset);
  3237.         return hr;
  3238.     }
  3239.     // Close the session
  3240.     void Close()
  3241.     {
  3242.         m_spOpenRowset.Release();
  3243.     }
  3244.     // Start a transaction
  3245.     HRESULT StartTransaction(ISOLEVEL isoLevel = ISOLATIONLEVEL_READCOMMITTED, ULONG isoFlags = 0,
  3246.         ITransactionOptions* pOtherOptions = NULL, ULONG* pulTransactionLevel = NULL) const
  3247.     {
  3248.         ATLASSERT(m_spOpenRowset != NULL);
  3249.         CComPtr<ITransactionLocal> spTransactionLocal;
  3250.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransactionLocal);
  3251.         
  3252.         if (SUCCEEDED(hr))
  3253.             hr = spTransactionLocal->StartTransaction(isoLevel, isoFlags, pOtherOptions, pulTransactionLevel);
  3254.  
  3255.         return hr;
  3256.     }
  3257.     // Abort the current transaction
  3258.     HRESULT Abort(BOID* pboidReason = NULL, BOOL bRetaining = FALSE, BOOL bAsync = FALSE) const
  3259.     {
  3260.         ATLASSERT(m_spOpenRowset != NULL);
  3261.         CComPtr<ITransaction> spTransaction;
  3262.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3263.  
  3264.         if (SUCCEEDED(hr))
  3265.             hr = spTransaction->Abort(pboidReason, bRetaining, bAsync);
  3266.  
  3267.         return hr;
  3268.     }
  3269.     // Commit the current transaction
  3270.     HRESULT Commit(BOOL bRetaining = FALSE, DWORD grfTC = XACTTC_SYNC, DWORD grfRM = 0) const
  3271.     {
  3272.         ATLASSERT(m_spOpenRowset != NULL);
  3273.         CComPtr<ITransaction> spTransaction;
  3274.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3275.  
  3276.         if (SUCCEEDED(hr))
  3277.             hr = spTransaction->Commit(bRetaining, grfTC, grfRM);
  3278.  
  3279.         return hr;
  3280.     }
  3281.     // Get information for the current transaction
  3282.     HRESULT GetTransactionInfo(XACTTRANSINFO* pInfo) const
  3283.     {
  3284.         ATLASSERT(m_spOpenRowset != NULL);
  3285.         CComPtr<ITransaction> spTransaction;
  3286.         HRESULT hr = m_spOpenRowset->QueryInterface(&spTransaction);
  3287.     
  3288.         if (SUCCEEDED(hr))
  3289.             hr = spTransaction->GetTransactionInfo(pInfo);
  3290.  
  3291.         return hr;
  3292.     }
  3293. // Implementation
  3294.     CComPtr<IOpenRowset> m_spOpenRowset;
  3295. };
  3296.  
  3297.  
  3298. ///////////////////////////////////////////////////////////////////////////
  3299. // CTable
  3300.  
  3301. template <class TAccessor = CNoAccessor, class TRowset = CRowset>
  3302. class CTable :
  3303.     public CAccessorRowset<TAccessor, TRowset>
  3304. {
  3305. public:
  3306.     // Open a rowset on the passed name
  3307.     HRESULT Open(const CSession& session, LPCTSTR szTableName, DBPROPSET* pPropSet = NULL)
  3308.     {
  3309.         USES_CONVERSION;
  3310.         DBID    idTable;
  3311.  
  3312.         idTable.eKind           = DBKIND_NAME;
  3313.         idTable.uName.pwszName  = (LPOLESTR)T2COLE(szTableName);
  3314.  
  3315.         return Open(session, idTable, pPropSet);
  3316.     }
  3317.     // Open the a rowset on the passed DBID
  3318.     HRESULT Open(const CSession& session, DBID& dbid, DBPROPSET* pPropSet = NULL)
  3319.     {
  3320.         // Check the session is valid
  3321.         ATLASSERT(session.m_spOpenRowset != NULL);
  3322.         HRESULT hr;
  3323.     
  3324.         hr = session.m_spOpenRowset->OpenRowset(NULL, &dbid, NULL, GetRowsetIID(),
  3325.             (pPropSet) ? 1 : 0, pPropSet, (IUnknown**)GetIRowsetPtr());
  3326.         if (SUCCEEDED(hr))
  3327.         {
  3328.             SetupOptionalRowsetInterfaces();
  3329.     
  3330.             // If we have output columns then bind
  3331.             if (_OutputColumnsClass::HasOutputColumns())
  3332.                 hr = Bind();
  3333.         }
  3334.  
  3335.         return hr;
  3336.     }
  3337. };
  3338.  
  3339. #if (OLEDBVER < 0x0150)
  3340. #define DBGUID_DEFAULT DBGUID_DBSQL
  3341. #endif
  3342.  
  3343.  
  3344. ///////////////////////////////////////////////////////////////////////////
  3345. // CCommandBase
  3346.  
  3347. class CCommandBase
  3348. {
  3349. public:
  3350.     CCommandBase()
  3351.     {
  3352.         m_hParameterAccessor = NULL;
  3353.     }
  3354.  
  3355.     ~CCommandBase()
  3356.     {
  3357.         ReleaseCommand();
  3358.     }
  3359.     // Create the command
  3360.     HRESULT CreateCommand(const CSession& session)
  3361.     {
  3362.         // Before creating the command, release the old one if necessary.
  3363.         ReleaseCommand();
  3364.  
  3365.         // Check the session is valid
  3366.         ATLASSERT(session.m_spOpenRowset != NULL);
  3367.  
  3368.         CComPtr<IDBCreateCommand> spCreateCommand;
  3369.  
  3370.         HRESULT hr = session.m_spOpenRowset->QueryInterface(IID_IDBCreateCommand, (void**)&spCreateCommand);
  3371.         if (FAILED(hr))
  3372.             return hr;
  3373.  
  3374.         return spCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&m_spCommand);
  3375.     }
  3376.     // Prepare the command
  3377.     HRESULT Prepare(ULONG cExpectedRuns = 0)
  3378.     {
  3379.         CComPtr<ICommandPrepare> spCommandPrepare;
  3380.         HRESULT hr = m_spCommand->QueryInterface(&spCommandPrepare);
  3381.         if (SUCCEEDED(hr))
  3382.             hr = spCommandPrepare->Prepare(cExpectedRuns);
  3383.         
  3384.         return hr;
  3385.     }
  3386.     // Unprepare the command
  3387.     HRESULT Unprepare()
  3388.     {
  3389.         CComPtr<ICommandPrepare> spCommandPrepare;
  3390.         HRESULT hr = m_spCommand->QueryInterface(&spCommandPrepare);
  3391.         if (SUCCEEDED(hr))
  3392.             hr = spCommandPrepare->Unprepare();
  3393.         
  3394.         return hr;
  3395.     }
  3396.     // Create the command and set the command text
  3397.     HRESULT Create(const CSession& session, LPCTSTR szCommand,
  3398.         REFGUID guidCommand = DBGUID_DEFAULT)
  3399.     {
  3400.         USES_CONVERSION;
  3401.         HRESULT hr;
  3402.  
  3403.         hr = CreateCommand(session);
  3404.         if (SUCCEEDED(hr))
  3405.         {
  3406.             CComPtr<ICommandText> spCommandText;
  3407.             hr = m_spCommand->QueryInterface(&spCommandText);
  3408.             if (SUCCEEDED(hr))
  3409.                 hr = spCommandText->SetCommandText(guidCommand, T2COLE(szCommand));
  3410.         }
  3411.         return hr;
  3412.     }
  3413.     // Release the command
  3414.     void ReleaseCommand()
  3415.     {
  3416.         // Release the parameter accessor if necessary, before releasing the command
  3417.         if (m_hParameterAccessor != NULL)
  3418.         {
  3419.             CComPtr<IAccessor> spAccessor;
  3420.             HRESULT hr = m_spCommand->QueryInterface(&spAccessor);
  3421.             if (SUCCEEDED(hr))
  3422.             {
  3423.                 spAccessor->ReleaseAccessor(m_hParameterAccessor, NULL); \
  3424.                 m_hParameterAccessor = NULL;
  3425.             }
  3426.         }
  3427.         m_spCommand.Release();
  3428.     }
  3429.     // Get the parameter information from the command
  3430.     HRESULT GetParameterInfo(ULONG* pParams, DBPARAMINFO** ppParamInfo,
  3431.                 OLECHAR** ppNamesBuffer)
  3432.     {
  3433.         CComPtr<ICommandWithParameters> spCommandParameters;
  3434.         HRESULT hr = m_spCommand->QueryInterface(&spCommandParameters);
  3435.         if (SUCCEEDED(hr))
  3436.         {
  3437.             // Get the parameter information
  3438.             hr = spCommandParameters->GetParameterInfo(pParams, ppParamInfo,
  3439.                     ppNamesBuffer);
  3440.         }
  3441.         return hr;        
  3442.     }
  3443.     // Set the parameter information for the command
  3444.     HRESULT SetParameterInfo(ULONG ulParams, const ULONG* pOrdinals,
  3445.                 const DBPARAMBINDINFO* pParamInfo)
  3446.     {
  3447.         CComPtr<ICommandWithParameters> spCommandParameters;
  3448.         HRESULT hr = m_spCommand->QueryInterface(&spCommandParameters);
  3449.         if (SUCCEEDED(hr))
  3450.         {
  3451.             // Set the parameter information
  3452.             hr = spCommandParameters->SetParameterInfo(ulParams, pOrdinals, 
  3453.                 pParamInfo);
  3454.         }
  3455.         return hr;        
  3456.     }
  3457.  
  3458.     CComPtr<ICommand>   m_spCommand;
  3459.     HACCESSOR           m_hParameterAccessor;
  3460. };
  3461.  
  3462. // Used to turn on multiple result set support in CCommand
  3463. class CMultipleResults
  3464. {
  3465. public:
  3466.     bool UseMultipleResults() { return true; }
  3467.     IMultipleResults** GetMultiplePtrAddress() { return &m_spMultipleResults; }
  3468.     
  3469.     CComPtr<IMultipleResults> m_spMultipleResults;
  3470. };
  3471.  
  3472. // Used to turn off multiple result set support in CCommand
  3473. class CNoMultipleResults
  3474. {
  3475. public:
  3476.     bool UseMultipleResults() { return false; }
  3477.     IMultipleResults** GetMultiplePtrAddress() { return NULL; }
  3478. };
  3479.  
  3480.  
  3481. ///////////////////////////////////////////////////////////////////////////
  3482. // CCommand
  3483.  
  3484. template <class TAccessor = CNoAccessor, class TRowset = CRowset, class TMultiple = CNoMultipleResults>
  3485. class CCommand :
  3486.     public CAccessorRowset<TAccessor, TRowset>,
  3487.     public CCommandBase,
  3488.     public TMultiple
  3489. {
  3490. public:
  3491.     // Create a command on the session and execute it
  3492.     HRESULT Open(const CSession& session, LPCTSTR szCommand = NULL,
  3493.         DBPROPSET *pPropSet = NULL, LONG* pRowsAffected = NULL,
  3494.         REFGUID guidCommand = DBGUID_DEFAULT, bool bBind = true)
  3495.     {
  3496.         HRESULT hr;
  3497.         if (szCommand == NULL)
  3498.         {
  3499.             hr = _CommandClass::GetDefaultCommand(&szCommand);
  3500.             if (FAILED(hr))
  3501.                 return hr;
  3502.         }
  3503.         hr = Create(session, szCommand, guidCommand);
  3504.         if (FAILED(hr))
  3505.             return hr;
  3506.         
  3507.         return Open(pPropSet, pRowsAffected, bBind);
  3508.     }
  3509.     // Used if you have previously created the command
  3510.     HRESULT Open(DBPROPSET *pPropSet = NULL, LONG* pRowsAffected = NULL, bool bBind = true)
  3511.     {
  3512.         HRESULT        hr;
  3513.         DBPARAMS    params;
  3514.         DBPARAMS    *pParams;
  3515.  
  3516.         // Bind the parameters if we have some
  3517.         if (_ParamClass::HasParameters())
  3518.         {
  3519.             // Bind the parameters in the accessor if they haven't already been bound
  3520.             hr = BindParameters(&m_hParameterAccessor, m_spCommand, ¶ms.pData);
  3521.             if (FAILED(hr))
  3522.                 return hr;
  3523.  
  3524.             // Setup the DBPARAMS structure
  3525.             params.cParamSets = 1;
  3526.             params.hAccessor = m_hParameterAccessor;
  3527.             pParams = ¶ms;
  3528.         }
  3529.         else
  3530.             pParams = NULL;
  3531.  
  3532.         hr = Execute(GetIRowsetPtr(), pParams, pPropSet, pRowsAffected);
  3533.         if (FAILED(hr))
  3534.             return hr;
  3535.  
  3536.         // Only bind if we have been asked to and we have output columns
  3537.         if (bBind && _OutputColumnsClass::HasOutputColumns())
  3538.             return Bind();
  3539.         else
  3540.             return hr;
  3541.     }
  3542.     // Get the next rowset when using multiple result sets
  3543.     HRESULT GetNextResult(LONG* pulRowsAffected, bool bBind = true)
  3544.     {
  3545.         // This function should only be called if CMultipleResults is being
  3546.         // used as the third template parameter
  3547.         ATLASSERT(GetMultiplePtrAddress() != NULL);
  3548.  
  3549.         // If user calls GetNextResult but the interface is not available
  3550.         // return E_FAIL.
  3551.         if (GetMultiplePtrAddress() == NULL || *GetMultiplePtrAddress() == NULL)
  3552.             return E_FAIL;
  3553.  
  3554.         // Close the existing rowset in preparation for opening the next one
  3555.         Close();
  3556.  
  3557.         HRESULT hr = (*GetMultiplePtrAddress())->GetResult(NULL, 0, IID_IRowset,
  3558.             pulRowsAffected, (IUnknown**)GetIRowsetPtr());
  3559.         if (FAILED(hr))
  3560.             return hr;
  3561.  
  3562.         if (bBind && GetIRowset() != NULL)
  3563.             return Bind();
  3564.         else
  3565.             return hr;
  3566.     }
  3567.  
  3568. // Implementation
  3569.     HRESULT Execute(IRowset** ppRowset, DBPARAMS* pParams, DBPROPSET *pPropSet, LONG* pRowsAffected)
  3570.     {
  3571.         HRESULT hr;
  3572.  
  3573.         // Specify the properties if we have some
  3574.         if (pPropSet)
  3575.         {
  3576.             CComPtr<ICommandProperties> spCommandProperties;
  3577.             hr = m_spCommand->QueryInterface(&spCommandProperties);
  3578.             if (FAILED(hr))
  3579.                 return hr;
  3580.  
  3581.             hr = spCommandProperties->SetProperties(1, pPropSet);
  3582.             if (FAILED(hr))
  3583.                 return hr;
  3584.         }
  3585.  
  3586.         // If the user want the rows affected then return it back, otherwise
  3587.         // just point to our local variable here.
  3588.         LONG nAffected, *pAffected;
  3589.         if (pRowsAffected)
  3590.             pAffected = pRowsAffected;
  3591.         else
  3592.             pAffected = &nAffected;
  3593.  
  3594.         if (UseMultipleResults())
  3595.         {
  3596.             hr = m_spCommand->Execute(NULL, IID_IMultipleResults, pParams,
  3597.                 pAffected, (IUnknown**)GetMultiplePtrAddress());
  3598.  
  3599.             if (SUCCEEDED(hr))
  3600.             {
  3601.                 hr = GetNextResult(pAffected, false);
  3602.             }
  3603.             else
  3604.             {
  3605.                 // If we can't get IMultipleResults then just try to get IRowset
  3606.                 hr = m_spCommand->Execute(NULL, IID_IRowset, pParams, pAffected, 
  3607.                     (IUnknown**)GetIRowsetPtr());
  3608.             }
  3609.         }
  3610.         else
  3611.         {
  3612.             hr = m_spCommand->Execute(NULL, GetRowsetIID(), pParams, pAffected,
  3613.                 (IUnknown**)ppRowset);
  3614.             if (SUCCEEDED(hr))
  3615.                 SetupOptionalRowsetInterfaces();
  3616.         }
  3617.         return hr;
  3618.     }
  3619. };
  3620.  
  3621.  
  3622. // This class can be used to implement the IRowsetNotify interface.
  3623. // It is supplied so that if you only want to implement one of the
  3624. // notifications you don't have to supply empty functions for the
  3625. // other methods.
  3626. class ATL_NO_VTABLE IRowsetNotifyImpl : public IRowsetNotify
  3627. {
  3628. public:
  3629.     STDMETHOD(OnFieldChange)(
  3630.             /* [in] */ IRowset* /* pRowset */,
  3631.             /* [in] */ HROW /* hRow */,
  3632.             /* [in] */ ULONG /* cColumns */,
  3633.             /* [size_is][in] */ ULONG /* rgColumns*/ [] ,
  3634.             /* [in] */ DBREASON /* eReason */,
  3635.             /* [in] */ DBEVENTPHASE /* ePhase */,
  3636.             /* [in] */ BOOL /* fCantDeny */)
  3637.     {
  3638.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnFieldChange"));
  3639.     }
  3640.     STDMETHOD(OnRowChange)(
  3641.             /* [in] */ IRowset* /* pRowset */,
  3642.             /* [in] */ ULONG /* cRows */,
  3643.             /* [size_is][in] */ const HROW /* rghRows*/ [] ,
  3644.             /* [in] */ DBREASON /* eReason */,
  3645.             /* [in] */ DBEVENTPHASE /* ePhase */,
  3646.             /* [in] */ BOOL /* fCantDeny */)
  3647.     {
  3648.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnRowChange"));
  3649.     }
  3650.     STDMETHOD(OnRowsetChange)(
  3651.         /* [in] */ IRowset* /* pRowset */,
  3652.         /* [in] */ DBREASON /* eReason */,
  3653.         /* [in] */ DBEVENTPHASE /* ePhase */,
  3654.         /* [in] */ BOOL /* fCantDeny*/)
  3655.     {
  3656.         ATLTRACENOTIMPL(_T("IRowsetNotifyImpl::OnRowsetChange"));
  3657.     }
  3658. };
  3659.  
  3660. }; //namespace ATL
  3661.  
  3662. #endif // __ATLDBCLI_H_
  3663.